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 "sparta/utils/MathUtils.hpp"
22
23namespace sparta
24{
52 {
53 public:
54 SpartaTester() : SpartaTester(0, std::cerr)
55 {
56 #if LOG_ERRORS_TO_FILE
57 static std::ofstream cerr_log_file("SpartaTester_errors.log"); // Ensure it stays open
58 if (cerr_log_file.is_open()) {
59 cerr_.rdbuf(cerr_log_file.rdbuf()); // Redirect cerr
60 } else {
61 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED
62 << "WARNING: Could not open SpartaTester_errors.log for writing. "
63 << "Errors will be printed to std::cerr."
64 << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
65 }
66 #endif
67 }
68
69 bool expectAllReached(uint32_t expected_reached, const uint32_t line, const char * file) {
70 bool ret = true;
71 if(methods_reached_.size() != expected_reached)
72 {
73 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test failed to execute the "
74 << expected_reached << " expected methods at least once." << "\n"
75 "Instead, " << methods_reached_.size() << " were reached."
76 << std::endl;
77 //list the methods that were in fact reached.
78 cerr_ << "The test only reached the following: " << std::endl;
79 cerr_ << SPARTA_CURRENT_COLOR_GREEN;
80 for(auto s : methods_reached_)
81 {
82 cerr_ << "-> " <<s << "\n";
83 }
84 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "' FAILED on line "
85 << line << " in file " << file << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
86 ++num_errors_;
87 ret = false;
88 cerr_ << std::endl;
89 }
90 return ret;
91 }
92 bool expect(bool val, const char * test_type, const uint32_t line, const char * file) {
93 bool ret = true;
94 if(!val) {
95 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
96 << line << " in file " << file << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
97 ++num_errors_;
98 ret = false;
99 }
100 return ret;
101 }
102
103 // Try and compare bytes and display values on failure instead of characters
104 bool expectEqual(uint8_t v1, uint8_t v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
105 bool ret = true;
106 if((v1 == v2) != expected) {
107 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
108 << line << " in file " << file
109 << ". Value: '" << (uint32_t)v1;
110 if(expected){
111 cerr_ << "' should equal '";
112 }else{
113 cerr_ << "' should NOT equal '";
114 }
115 cerr_ << (uint32_t)v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
116 ++num_errors_;
117 ret = false;
118 }
119 return ret;
120 }
121
122 // Try and compare different types
123 template<typename T>
124 bool expectEqual(const T& v1, const T& v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
125 bool ret = true;
126 if((v1 == v2) != expected) {
127 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
128 << line << " in file " << file
129 << ". Value: '" << v1;
130 if(expected){
131 cerr_ << "' should equal '";
132 }else{
133 cerr_ << "' should NOT equal '";
134 }
135 cerr_ << v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
136 ++num_errors_;
137 ret = false;
138 }
139 return ret;
140 }
141
142 // Try and compare different types
143 template<typename T, typename U=T>
144 bool expectEqual(const T& v1, const U& v2, bool expected, const char * test_type, const uint32_t line, const char * file) {
145 bool ret = true;
146 if(compare<T,U>(v1,v2) != expected) {
147 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
148 << line << " in file " << file
149 << ". Value: '" << v1;
150 if(expected){
151 cerr_ << "' should equal '";
152 }else{
153 cerr_ << "' should NOT equal '";
154 }
155 cerr_ << v2 << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
156 ++num_errors_;
157 ret = false;
158 }
159 return ret;
160 }
161
162 // Comparison operation for two integers having different signed-ness
163 template<typename T, typename U>
164 typename std::enable_if<std::is_integral<T>::value
165 && std::is_integral<U>::value
166 && (std::is_signed<T>::value != std::is_signed<U>::value), bool>::type
167 compare(const T& t, const U& u) {
168 return (t == (T)u);
169 }
170
171 // Comparison operation for everything else
172 template<typename T, typename U>
173 typename std::enable_if<!(std::is_integral<T>::value
174 && std::is_integral<U>::value
175 && (std::is_signed<T>::value != std::is_signed<U>::value)), bool>::type
176 compare(const T& t, const U& u) {
177 return (t == u);
178 }
179
180
181 // Overload for comparison with nullptr
182 template<typename T>
183 bool expectEqual(const T& v1, const std::nullptr_t, bool expected, const char * test_type, const uint32_t line, const char * file) {
184 bool ret = true;
185 if((v1 == nullptr) != expected) {
186 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
187 << line << " in file " << file
188 << ". Value: '" << v1;
189 if(expected){
190 cerr_ << "' should equal '";
191 }else{
192 cerr_ << "' should NOT equal '";
193 }
194 cerr_ << "null" << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
195 ++num_errors_;
196 ret = false;
197 }
198 return ret;
199 }
200
201 // Overload for comparison of nullptr with a var
202 template<typename T>
203 bool expectEqual(const std::nullptr_t, const T& v1, bool expected, const char * test_type, const uint32_t line, const char * file) {
204 bool ret = true;
205 if((nullptr == v1) != expected) {
206 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '" << test_type << "' FAILED on line "
207 << line << " in file " << file
208 << ". Value: '" << v1;
209 if(expected){
210 cerr_ << "' should equal '";
211 }else{
212 cerr_ << "' should NOT equal '";
213 }
214 cerr_ << "null" << "'" << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
215 ++num_errors_;
216 ret = false;
217 }
218 return ret;
219 }
220
221 template<typename T>
222 typename std::enable_if<
223 std::is_floating_point<T>::value,
224 bool>::type
225 expectEqualWithinTolerance(const T & v1, const T & v2, const T & tol,
226 const char * test_type, const uint32_t line,
227 const char * file) {
228 bool ret = true;
229 if (tol < 0) {
230 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '"
231 << test_type << "' FAILED on line "
232 << line << " in file " << file
233 << ". Negative tolerance supplied."
234 << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
235 ++num_errors_;
236 ret = false;
237 } else {
238 ret = utils::approximatelyEqual(v1, v2, tol);
239 if (!ret) {
240 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Test '"
241 << test_type << "' FAILED on line "
242 << line << " in file " << file
243 << ". Value: '" << v1 << "' should be equal to '"
244 << v2 << "' within tolerance '" << tol << "'";
245 ++num_errors_;
246 }
247 }
248 return ret;
249 }
250
251 void throwTestFailed(const char * test_type, const uint32_t line, const char * file, const char * exception_what="") {
252 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "Throw Test Fail:'" << test_type << "' FAILED on line "
253 << line << " in file " << file << std::endl;
254 if(exception_what != 0 && strlen(exception_what) != 0) {
255 cerr_ << " Exception: " << exception_what << std::endl;
256 }
257 cerr_ << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
258 ++num_errors_;
259 }
260
280 void expectFilesEqual(const std::string& a, const std::string& b, bool expected, uint32_t line, const char * file, const bool ignore_commented_lines = true) {
281 std::ifstream fa, fb;
282 std::stringstream err;
283 try{
284 fa.open(a, std::ios_base::in);
285 }catch(std::exception&){
286 }
287 if(fa.fail()){
288 err.str("");
289 err << "Could not open file \"" << a << "\"";
290 fileComparisonFailed(a, b, line, file, err.str());
291 }
292 try{
293 fb.open(b, std::ios_base::in);
294 }catch(std::exception&){
295 }
296 if(fb.fail()){
297 err.str("");
298 err << "Could not open file \"" << b << "\"";
299 fileComparisonFailed(a, b, line, file, err.str());
300 }
301
302 if(!fa.fail() && !fb.fail()){
303 uint32_t line_num = 0;
304 uint32_t last_line_pos = 0;
305 uint64_t pos = 0;
306 bool was_newline = true;
307 while(1){
308 char cho, chn;
309 cho = fa.get();
310 chn = fb.get();
311
312 // Ignore lines starting with '#'
313 if(was_newline && ignore_commented_lines){
314 was_newline = false;
315 if(cho == '#'){
316 while(1){
317 // Read until after newline
318 cho = fa.get();
319 if(cho == '\n'){
320 cho = fa.get();
321 if(cho != '#'){
322 break;
323 }
324 }
325 ++pos; // Increment pos on this file, but not the other
326 }
327 }
328 if(chn == '#'){
329 // Read until after newline
330 while(1){
331 chn = fb.get();
332 if(chn == '\n'){
333 chn = fb.get();
334 if(chn != '#'){
335 break;
336 }
337 }
338 }
339 }
340 }
341
342 if(!fa.good() || !fb.good()){
343 if((fa.good() != fb.good()) && expected == true){
344 std::stringstream msg;
345 msg << "Files were different lengths: ";
346 if(!fa.good()){
347 msg << a << " was shorter than " << b << " at char '" << chn << "' #" << pos;
348 }else{
349 msg << b << " was shorter than " << a << " at char '" << cho << "' #" << pos;
350 }
351 fileComparisonFailed(a, b, line, file, msg.str());
352 }
353 break;
354 }
355
356 if(cho != chn){
357 err.str("");
358 err << "Files differed at pos " << pos << " (line "
359 << line_num << ", col " << pos - last_line_pos
360 << ") with chars: '" << cho << "' != '" << chn << "'";
361 if(expected == true){
362 fileComparisonFailed(a, b, line, file, err.str());
363 }
364 return;
365 }
366 ++pos;
367 if(cho == '\n'){ // prev char (pos-1)
368 ++line_num;
369 last_line_pos = pos; // Line starts here
370 was_newline = true;
371 }
372 }
373
374 if(expected == false){
375 fileComparisonFailed(a, b, line, file, "Files were the same");
376 }
377 }
378 }
379
380 void fileComparisonFailed(const std::string& a, const std::string& b, const uint32_t line, const char * file, const std::string& error) {
381 cerr_ << SPARTA_CURRENT_COLOR_BRIGHT_RED << "File comparison test between \"" << a << "\" and \"" << b << "\" FAILED on line "
382 << line << " in file " << file << std::endl;
383 cerr_ << " Exception: " << error << std::endl;
384 cerr_ << SPARTA_CURRENT_COLOR_NORMAL << std::endl;
385 ++num_errors_;
386 }
387
388 void reachedMethod(const std::string& method_title)
389 {
390 methods_reached_.insert(method_title);
391 }
392 static SpartaTester * getInstance() {
393 static SpartaTester inst;
394 return & inst;
395 }
396
397 static std::unique_ptr<SpartaTester> makeTesterWithUserCError(std::ostream & cerr) {
398 return std::unique_ptr<SpartaTester>(new SpartaTester(0, cerr));
399 }
400
401 static uint32_t getErrorCode(const SpartaTester * tester = getInstance()) {
402 return tester->num_errors_;
403 }
404
405 private:
406 SpartaTester(const uint32_t num_errors,
407 std::ostream & cerr) :
408 num_errors_(num_errors),
409 methods_reached_(),
410 cerr_(cerr)
411 {}
412
413 uint32_t num_errors_;
414 std::set<std::string> methods_reached_;
415 std::ostream & cerr_;
416 };
417
418
424#define TEST_INIT
425
426
432#define EXPECT_REACHED() sparta::SpartaTester::getInstance()->reachedMethod(__FUNCTION__)
433
434
441#define ENSURE_ALL_REACHED(x) sparta::SpartaTester::getInstance()->expectAllReached(x, __LINE__, __FUNCTION__)
442
443
458#define EXPECT_TRUE(x) sparta::SpartaTester::getInstance()->expect((x), #x, __LINE__, __FILE__)
459
478#define EXPECT_EQUAL(x, y) sparta::SpartaTester::getInstance()->expectEqual((x), (y), true, #x, __LINE__, __FILE__)
479
498#define EXPECT_NOTEQUAL(x, y) sparta::SpartaTester::getInstance()->expectEqual((x), (y), false, #x, __LINE__, __FILE__)
499
520#define EXPECT_WITHIN_TOLERANCE(x, y, tol) sparta::SpartaTester::getInstance()-> \
521 expectEqualWithinTolerance((x), (y), (tol), #x, __LINE__, __FILE__)
522
544#define EXPECT_WITHIN_EPSILON(x, y) sparta::SpartaTester::getInstance()-> \
545 expectEqualWithinTolerance((x), (y), \
546 (std::numeric_limits<decltype(x)>::epsilon()), #x, __LINE__, __FILE__)
547
562#define EXPECT_FALSE(x) sparta::SpartaTester::getInstance()->expect(!(x), #x, __LINE__, __FILE__)
563
573#define EXPECT_THROW(x) { \
574 bool did_it_throw = false; \
575 try {x;} \
576 catch(...) \
577 { did_it_throw = true; } \
578 if(did_it_throw == false) { \
579 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__); \
580 } \
581 }
582
601#define EXPECT_THROW_MSG_SHORT(x, expected_msg) { \
602 bool did_it_throw = false; \
603 try { x; } \
604 catch(sparta::SpartaException& ex) \
605 { did_it_throw = true; \
606 if (strcmp(expected_msg, ex.rawReason().c_str()) != 0) { \
607 std::cerr << "Expected msg: " << expected_msg << std::endl; \
608 std::cerr << "Actual msg: " << ex.what() << std::endl; \
609 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
610 } \
611 } \
612 if(did_it_throw == false) { \
613 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
614 } \
615 }
616
635#define EXPECT_THROW_MSG_LONG(x, expected_msg) { \
636 bool did_it_throw = false; \
637 try { x; } \
638 catch(sparta::SpartaException& ex) \
639 { did_it_throw = true; \
640 if (strcmp(expected_msg, ex.what()) != 0) { \
641 std::cerr << "Expected msg: " << expected_msg << std::endl; \
642 std::cerr << "Actual msg: " << ex.what() << std::endl; \
643 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
644 } \
645 } \
646 if(did_it_throw == false) { \
647 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
648 } \
649 }
650
651#define EXPECT_THROW_MSG_CONTAINS(x, expected_msg) { \
652 bool did_it_throw = false; \
653 try { x; } \
654 catch(sparta::SpartaException& ex) \
655 { did_it_throw = true; \
656 if (boost::algorithm::contains(ex.what(), expected_msg) != true) { \
657 std::cerr << "Expected msg: " << expected_msg << std::endl; \
658 std::cerr << "Actual msg: " << ex.what() << std::endl; \
659 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, ex.what()); \
660 } \
661 } \
662 if(did_it_throw == false) { \
663 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, "did not throw"); \
664 } \
665 }
666
667
668
669
679#define EXPECT_NOTHROW(x) { \
680 bool did_it_throw = false; \
681 std::string exception_what; \
682 try { x; } \
683 catch(std::exception& e) \
684 { did_it_throw = true; \
685 exception_what = e.what(); \
686 } \
687 catch(...) \
688 { did_it_throw = true; } \
689 if(did_it_throw == true) { \
690 sparta::SpartaTester::getInstance()->throwTestFailed(#x, __LINE__, __FILE__, exception_what.c_str()); \
691 } \
692 }
693
704#define EXPECT_FILES_EQUAL(a, b) { \
705 sparta::SpartaTester::getInstance()->expectFilesEqual(a, b, true, __LINE__, __FILE__); \
706 }
707
718#define EXPECT_FILES_NOTEQUAL(a, b) { \
719 sparta::SpartaTester::getInstance()->expectFilesEqual(a, b, false, __LINE__, __FILE__); \
720 }
721
726#define ERROR_CODE sparta::SpartaTester::getErrorCode()
727
749#define REPORT_ERROR \
750 if(ERROR_CODE != 0) { \
751 std::cout << std::dec << "\n" << SPARTA_UNMANAGED_COLOR_BRIGHT_RED << ERROR_CODE \
752 << " ERROR(S) found during test.\n" << SPARTA_UNMANAGED_COLOR_NORMAL << std::endl; \
753 } else { \
754 std::cout << std::dec << "\n" \
755 << "TESTS PASSED -- No errors found during test.\n" << std::endl; \
756 }
757}
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.