1 /* 2 ------------------------------------------------------------------------- 3 CxxTest: A lightweight C++ unit testing library. 4 Copyright (c) 2008 Sandia Corporation. 5 This software is distributed under the LGPL License v3 6 For more information, see the COPYING file in the top CxxTest directory. 7 Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 8 the U.S. Government retains certain rights in this software. 9 ------------------------------------------------------------------------- 10 */ 11 12 // Licensed under the LGPL, see http://www.gnu.org/licenses/lgpl.html 13 14 #ifndef __CXXTEST__XMLFORMATTER_H 15 #define __CXXTEST__XMLFORMATTER_H 16 17 // 18 // The XmlFormatter is a TestListener that 19 // prints reports of the errors to an output 20 // stream in the form of an XML document. 21 // 22 23 // The following definitions are used if stack trace support is enabled, 24 // to give the traces an easily-parsable XML format. If stack tracing is 25 // not enabled, then these definitions will be ignored. 26 #define CXXTEST_STACK_TRACE_ESCAPE_AS_XML 27 #define CXXTEST_STACK_TRACE_NO_ESCAPE_FILELINE_AFFIXES 28 29 #define CXXTEST_STACK_TRACE_INITIAL_PREFIX "<stack-frame function=\"" 30 #define CXXTEST_STACK_TRACE_INITIAL_SUFFIX "\"/>\n" 31 #define CXXTEST_STACK_TRACE_OTHER_PREFIX CXXTEST_STACK_TRACE_INITIAL_PREFIX 32 #define CXXTEST_STACK_TRACE_OTHER_SUFFIX CXXTEST_STACK_TRACE_INITIAL_SUFFIX 33 #define CXXTEST_STACK_TRACE_ELLIDED_MESSAGE "" 34 #define CXXTEST_STACK_TRACE_FILELINE_PREFIX "\" location=\"" 35 #define CXXTEST_STACK_TRACE_FILELINE_SUFFIX "" 36 37 38 #include <cxxtest/TestRunner.h> 39 #include <cxxtest/TestListener.h> 40 #include <cxxtest/TestTracker.h> 41 #include <cxxtest/ValueTraits.h> 42 #include <cxxtest/ErrorFormatter.h> 43 #include <cxxtest/StdHeaders.h> 44 #include <iostream> 45 #include <sstream> 46 #include <cstring> 47 #include <cstdio> 48 #include <ctime> 49 50 namespace CxxTest 51 { 52 class TeeOutputStreams 53 { 54 private: 55 class teebuffer : public std::basic_streambuf<char> 56 { 57 typedef std::basic_streambuf<char> streambuf_t; 58 public: teebuffer(streambuf_t * buf1,streambuf_t * buf2)59 teebuffer(streambuf_t * buf1, streambuf_t * buf2) 60 : buffer1(buf1), buffer2(buf2) 61 {} 62 overflow(int c)63 virtual int overflow(int c) 64 { 65 if (c == EOF) 66 { 67 return !EOF; 68 } 69 else 70 { 71 int const ans1 = buffer1->sputc(c); 72 int const ans2 = buffer2->sputc(c); 73 return ans1 == EOF || ans2 == EOF ? EOF : c; 74 } 75 } 76 sync()77 virtual int sync() 78 { 79 int ans1 = buffer1->pubsync(); 80 int ans2 = buffer2->pubsync(); 81 return ans1 || ans2 ? -1 : 0; 82 } 83 84 streambuf_t * buffer1; 85 streambuf_t * buffer2; 86 }; 87 88 public: TeeOutputStreams(std::ostream & _cout,std::ostream & _cerr)89 TeeOutputStreams(std::ostream& _cout, std::ostream& _cerr) 90 : out(), 91 err(), 92 orig_cout(_cout), 93 orig_cerr(_cerr), 94 tee_out(out.rdbuf(), _cout.rdbuf()), 95 tee_err(err.rdbuf(), _cerr.rdbuf()) 96 { 97 orig_cout.rdbuf(&tee_out); 98 orig_cerr.rdbuf(&tee_err); 99 } 100 ~TeeOutputStreams()101 ~TeeOutputStreams() 102 { 103 orig_cout.rdbuf(tee_out.buffer2); 104 orig_cerr.rdbuf(tee_err.buffer2); 105 } 106 107 std::stringstream out; 108 std::stringstream err; 109 110 private: 111 std::ostream& orig_cout; 112 std::ostream& orig_cerr; 113 teebuffer tee_out; 114 teebuffer tee_err; 115 }; 116 117 class ElementInfo 118 { 119 public: 120 std::string name; 121 std::stringstream value; 122 std::map<std::string, std::string> attribute; 123 ElementInfo()124 ElementInfo() 125 : name(), value(), attribute() 126 {} 127 ElementInfo(const ElementInfo & rhs)128 ElementInfo(const ElementInfo& rhs) 129 : name(rhs.name), value(rhs.value.str()), attribute(rhs.attribute) 130 {} 131 132 ElementInfo& operator=(const ElementInfo& rhs) 133 { 134 name = rhs.name; 135 value.str(rhs.value.str()); 136 attribute = rhs.attribute; 137 return *this; 138 } 139 140 template <class Type> add(const std::string & name_,Type & value_)141 void add(const std::string& name_, Type& value_) 142 { 143 std::ostringstream os; 144 os << value_; 145 attribute[name_] = os.str(); 146 } 147 write(OutputStream & os)148 void write(OutputStream& os) 149 { 150 os << " <" << name.c_str() << " "; 151 std::map<std::string, std::string>::iterator curr = attribute.begin(); 152 std::map<std::string, std::string>::iterator end = attribute.end(); 153 while (curr != end) 154 { 155 os << curr->first.c_str() 156 << "=\"" << curr->second.c_str() << "\" "; 157 curr++; 158 } 159 if (value.str().empty()) 160 { 161 os << "/>"; 162 } 163 else 164 { 165 os << ">" << escape(value.str()).c_str() 166 << "</" << name.c_str() << ">"; 167 } 168 os.endl(os); 169 } 170 escape(const std::string & str)171 std::string escape(const std::string& str) 172 { 173 std::string escStr = ""; 174 for (size_t i = 0; i < str.length(); i++) 175 { 176 switch (str[i]) 177 { 178 case '"': escStr += """; break; 179 case '\'': escStr += "'"; break; 180 case '<': escStr += "<"; break; 181 case '>': escStr += ">"; break; 182 case '&': escStr += "&"; break; 183 default: escStr += str[i]; break; 184 } 185 } 186 return escStr; 187 } 188 189 }; 190 191 class TestCaseInfo 192 { 193 public: 194 TestCaseInfo()195 TestCaseInfo() : fail(false), error(false), runtime(0.0) {} 196 std::string className; 197 std::string testName; 198 std::string line; 199 bool fail; 200 bool error; 201 double runtime; 202 std::list<ElementInfo> elements; 203 typedef std::list<ElementInfo>::iterator element_t; 204 std::string world; 205 add_element(const std::string & name)206 element_t add_element(const std::string& name) 207 { 208 element_t elt = elements.insert(elements.end(), ElementInfo()); 209 elt->name = name; 210 return elt; 211 } 212 update_element(const std::string & name)213 element_t update_element(const std::string& name) 214 { 215 element_t elt = elements.begin(); 216 while (elt != elements.end()) 217 { 218 if (elt->name == name) 219 { 220 return elt; 221 } 222 elt++; 223 } 224 return add_element(name); 225 } 226 write(OutputStream & o)227 void write(OutputStream &o) 228 { 229 o << " <testcase classname=\"" << className.c_str() 230 << "\" name=\"" << testName.c_str() 231 << "\" line=\"" << line.c_str() << "\""; 232 bool elts = false; 233 element_t curr = elements.begin(); 234 element_t end = elements.end(); 235 while (curr != end) 236 { 237 if (!elts) 238 { 239 o << ">"; 240 o.endl(o); 241 elts = true; 242 } 243 curr->write(o); 244 curr++; 245 } 246 if (elts) 247 { 248 o << " </testcase>"; 249 } 250 else 251 { 252 o << " />"; 253 } 254 o.endl(o); 255 } 256 257 }; 258 259 class XmlFormatter : public TestListener 260 { 261 public: XmlFormatter(OutputStream * o,OutputStream * ostr,std::ostringstream * os)262 XmlFormatter(OutputStream *o, OutputStream *ostr, std::ostringstream *os) 263 : _o(o), _ostr(ostr), _os(os), stream_redirect(NULL) 264 { 265 testcase = info.end(); 266 } 267 268 std::list<TestCaseInfo> info; 269 std::list<TestCaseInfo>::iterator testcase; 270 typedef std::list<ElementInfo>::iterator element_t; 271 std::string classname; 272 int ntests; 273 int nfail; 274 int nerror; 275 double totaltime; 276 run()277 int run() 278 { 279 TestRunner::runAllTests(*this); 280 return tracker().failedTests(); 281 } 282 enterWorld(const WorldDescription &)283 void enterWorld(const WorldDescription & /*desc*/) 284 { 285 ntests = 0; 286 nfail = 0; 287 nerror = 0; 288 totaltime = 0; 289 } 290 totalTests(OutputStream & o)291 static void totalTests(OutputStream &o) 292 { 293 char s[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; 294 const WorldDescription &wd = tracker().world(); 295 o << wd.strTotalTests(s) 296 << (wd.numTotalTests() == 1 ? " test" : " tests"); 297 } 298 enterSuite(const SuiteDescription & desc)299 void enterSuite(const SuiteDescription& desc) 300 { 301 classname = desc.suiteName(); 302 // replace "::" namespace with java-style "." 303 size_t pos = 0; 304 while ((pos = classname.find("::", pos)) != 305 CXXTEST_STD(string::npos)) 306 { 307 classname.replace(pos, 2, "."); 308 } 309 while (! classname.empty() && classname[0] == '.') 310 { 311 classname.erase(0, 1); 312 } 313 314 //CXXTEST_STD(cout) << "HERE " << desc.file() << " " 315 // << classname << CXXTEST_STD(endl); 316 317 //classname=desc.suiteName(); 318 //(*_o) << "file=\"" << desc.file() << "\" "; 319 //(*_o) << "line=\"" << desc.line() << "\""; 320 //_o->flush(); 321 } 322 leaveSuite(const SuiteDescription &)323 void leaveSuite(const SuiteDescription &) 324 { 325 std::list<TestCaseInfo>::iterator curr = info.begin(); 326 std::list<TestCaseInfo>::iterator end = info.end(); 327 while (curr != end) 328 { 329 if (curr->fail) { nfail++; } 330 if (curr->error) { nerror++; } 331 totaltime += curr->runtime; 332 ntests++; 333 curr++; 334 } 335 curr = info.begin(); 336 end = info.end(); 337 while (curr != end) 338 { 339 (*curr).write(*_ostr); 340 curr++; 341 } 342 info.clear(); 343 } 344 enterTest(const TestDescription & desc)345 void enterTest(const TestDescription & desc) 346 { 347 testcase = info.insert(info.end(), TestCaseInfo()); 348 testcase->testName = desc.testName(); 349 testcase->className = classname; 350 std::ostringstream os; 351 os << desc.line(); 352 testcase->line = os.str(); 353 354 if (stream_redirect) 355 CXXTEST_STD(cerr) << "ERROR: The stream_redirect != NULL" 356 << CXXTEST_STD(endl); 357 358 stream_redirect = 359 new TeeOutputStreams(CXXTEST_STD(cout), CXXTEST_STD(cerr)); 360 } 361 leaveTest(const TestDescription &)362 void leaveTest(const TestDescription &) 363 { 364 if (stream_redirect != NULL) 365 { 366 std::string out = stream_redirect->out.str(); 367 if (! out.empty()) 368 { 369 // silently ignore the '.' 370 if (out[0] != '.' || out.size() > 1) 371 { 372 testcase->add_element("system-out")->value << out; 373 } 374 } 375 if (! stream_redirect->err.str().empty()) 376 { 377 testcase->add_element("system-err")->value << stream_redirect->err.str(); 378 } 379 380 delete stream_redirect; 381 stream_redirect = NULL; 382 } 383 } 384 leaveWorld(const WorldDescription & desc)385 void leaveWorld(const WorldDescription& desc) 386 { 387 std::ostringstream os; 388 const std::string currentDateTime = currentDateTimeStr(); 389 os << totaltime; 390 (*_o) << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl; 391 (*_o) << "<testsuite name=\"" << desc.worldName() << "\" "; 392 (*_o) << "date=\"" << currentDateTime.c_str(); 393 (*_o) << "\" tests=\"" << ntests 394 << "\" errors=\"" << nerror 395 << "\" failures=\"" << nfail 396 << "\" time=\"" << os.str().c_str() << "\" >"; 397 _o->endl(*_o); 398 (*_o) << _os->str().c_str(); 399 _os->clear(); 400 (*_o) << "</testsuite>" << endl; 401 _o->flush(); 402 } 403 trace(const char *,int line,const char * expression)404 void trace(const char* /*file*/, int line, const char *expression) 405 { 406 if (testcase == info.end()) { 407 return; 408 } 409 element_t elt = testcase->add_element("trace"); 410 elt->add("line", line); 411 elt->value << expression; 412 } 413 warning(const char *,int line,const char * expression)414 void warning(const char* /*file*/, int line, const char *expression) 415 { 416 if (testcase == info.end()) { 417 return; 418 } 419 element_t elt = testcase->add_element("warning"); 420 elt->add("line", line); 421 elt->value << expression; 422 } 423 skippedTest(const char * file,int line,const char * expression)424 void skippedTest(const char* file, int line, const char* expression) 425 { 426 testSkipped(file, line, "skipped") << "Test skipped: " << expression; 427 } 428 failedTest(const char * file,int line,const char * expression)429 void failedTest(const char* file, int line, const char* expression) 430 { 431 testFailure(file, line, "failure") << "Test failed: " << expression; 432 } 433 failedAssert(const char * file,int line,const char * expression)434 void failedAssert(const char *file, int line, const char *expression) 435 { 436 testFailure(file, line, "failedAssert") 437 << "Assertion failed: " << expression; 438 } 439 failedAssertEquals(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)440 void failedAssertEquals(const char *file, int line, 441 const char* xStr, const char* yStr, 442 const char *x, const char *y) 443 { 444 testFailure(file, line, "failedAssertEquals") 445 << "Error: Expected (" 446 << xStr << " == " << yStr << "), found (" 447 << x << " != " << y << ")"; 448 } 449 failedAssertSameData(const char * file,int line,const char * xStr,const char * yStr,const char * sizeStr,const void *,const void *,unsigned size)450 void failedAssertSameData(const char *file, int line, 451 const char *xStr, const char *yStr, const char *sizeStr, 452 const void* /*x*/, const void* /*y*/, unsigned size) 453 { 454 testFailure(file, line, "failedAssertSameData") 455 << "Error: Expected " << sizeStr 456 << " (" << size << ") bytes to be equal at (" 457 << xStr << ") and (" << yStr << "), found"; 458 } 459 failedAssertSameFiles(const char * file,int line,const char *,const char *,const char * explanation)460 void failedAssertSameFiles(const char *file, int line, 461 const char *, const char *, 462 const char* explanation 463 ) 464 { 465 testFailure(file, line, "failedAssertSameFiles") 466 << "Error: " << explanation; 467 } 468 failedAssertDelta(const char * file,int line,const char * xStr,const char * yStr,const char * dStr,const char * x,const char * y,const char * d)469 void failedAssertDelta(const char *file, int line, 470 const char *xStr, const char *yStr, const char *dStr, 471 const char *x, const char *y, const char *d) 472 { 473 testFailure(file, line, "failedAssertDelta") 474 << "Error: Expected (" 475 << xStr << " == " << yStr << ") up to " << dStr 476 << " (" << d << "), found (" 477 << x << " != " << y << ")"; 478 } 479 failedAssertDiffers(const char * file,int line,const char * xStr,const char * yStr,const char * value)480 void failedAssertDiffers(const char *file, int line, 481 const char *xStr, const char *yStr, 482 const char *value) 483 { 484 testFailure(file, line, "failedAssertDiffers") 485 << "Error: Expected (" 486 << xStr << " != " << yStr << "), found (" 487 << value << ")"; 488 } 489 failedAssertLessThan(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)490 void failedAssertLessThan(const char *file, int line, 491 const char *xStr, const char *yStr, 492 const char *x, const char *y) 493 { 494 testFailure(file, line, "failedAssertLessThan") 495 << "Error: Expected (" << 496 xStr << " < " << yStr << "), found (" << 497 x << " >= " << y << ")"; 498 } 499 failedAssertLessThanEquals(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)500 void failedAssertLessThanEquals(const char *file, int line, 501 const char *xStr, const char *yStr, 502 const char *x, const char *y) 503 { 504 testFailure(file, line, "failedAssertLessThanEquals") 505 << "Error: Expected (" << 506 xStr << " <= " << yStr << "), found (" << 507 x << " > " << y << ")"; 508 } 509 failedAssertRelation(const char * file,int line,const char * relation,const char * xStr,const char * yStr,const char * x,const char * y)510 void failedAssertRelation(const char *file, int line, 511 const char *relation, const char *xStr, const char *yStr, 512 const char *x, const char *y) 513 { 514 testFailure(file, line, "failedAssertRelation") 515 << "Error: Expected " << relation << "( " << 516 xStr << ", " << yStr << " ), found !" << relation 517 << "( " << x << ", " << y << " )"; 518 } 519 failedAssertPredicate(const char * file,int line,const char * predicate,const char * xStr,const char * x)520 void failedAssertPredicate(const char *file, int line, 521 const char *predicate, const char *xStr, const char *x) 522 { 523 testFailure(file, line, "failedAssertPredicate") 524 << "Error: Expected " << predicate << "( " << 525 xStr << " ), found !" << predicate << "( " << x << " )"; 526 } 527 failedAssertThrows(const char * file,int line,const char * expression,const char * type,bool otherThrown)528 void failedAssertThrows(const char *file, int line, 529 const char *expression, const char *type, 530 bool otherThrown) 531 { 532 testFailure(file, line, "failedAssertThrows") 533 << "Error: Expected (" << expression << ") to throw (" << 534 type << ") but it " 535 << (otherThrown ? "threw something else" : "didn't throw"); 536 } 537 failedAssertThrowsNot(const char * file,int line,const char * expression)538 void failedAssertThrowsNot(const char *file, int line, const char *expression) 539 { 540 testFailure(file, line, "failedAssertThrowsNot") 541 << "Error: Expected (" << expression 542 << ") not to throw, but it did"; 543 } 544 545 protected: 546 outputStream()547 OutputStream *outputStream() const 548 { 549 return _o; 550 } 551 outputFileStream()552 OutputStream *outputFileStream() const 553 { 554 return _ostr; 555 } 556 557 private: 558 XmlFormatter(const XmlFormatter &); 559 XmlFormatter &operator=(const XmlFormatter &); 560 testFailure(const char * file,int line,const char * failureType)561 std::stringstream& testFailure(const char* file, int line, const char *failureType) 562 { 563 testcase->fail = true; 564 element_t elt = testcase->update_element("failure"); 565 if (elt->value.str().empty()) 566 { 567 elt->add("type", failureType); 568 elt->add("line", line); 569 elt->add("file", file); 570 } 571 else 572 { 573 elt->value << CXXTEST_STD(endl); 574 } 575 return elt->value; 576 //failedTest(file,line,message.c_str()); 577 } 578 testSkipped(const char * file,int line,const char * failureType)579 std::stringstream& testSkipped(const char* file, int line, const char *failureType) 580 { 581 //testcase->fail = true; 582 element_t elt = testcase->update_element("skipped"); 583 if (elt->value.str().empty()) 584 { 585 elt->add("type", failureType); 586 elt->add("line", line); 587 elt->add("file", file); 588 } 589 else 590 { 591 elt->value << CXXTEST_STD(endl); 592 } 593 return elt->value; 594 } 595 currentDateTimeStr()596 std::string currentDateTimeStr() 597 { 598 std::string retVal; 599 const time_t now(time(NULL)); 600 char current_date_string[27]; 601 602 #ifdef WIN32 603 if (ctime_s(current_date_string, sizeof(current_date_string)-1, &now) == 0) 604 { 605 retVal = current_date_string; 606 retVal.erase(retVal.find_last_not_of(" \n\r\t")+1); // trim trailing whitespace 607 } 608 #else 609 const size_t n = strlen(ctime_r(&now, current_date_string)); 610 if (n) 611 { 612 current_date_string[n-1] = '\0'; // remove the ending \n 613 retVal = current_date_string; 614 } 615 #endif 616 return retVal; 617 } 618 619 #if 0 620 void attributeBinary(const char* name, const void *value, unsigned size) 621 { 622 (*_o) << name; 623 (*_o) << "=\""; 624 dump(value, size); 625 (*_o) << "\" "; 626 } 627 628 void dump(const void *buffer, unsigned size) 629 { 630 if (!buffer) { return; } 631 632 unsigned dumpSize = size; 633 if (maxDumpSize() && dumpSize > maxDumpSize()) 634 { 635 dumpSize = maxDumpSize(); 636 } 637 638 const unsigned char *p = (const unsigned char *)buffer; 639 for (unsigned i = 0; i < dumpSize; ++ i) 640 { 641 (*_o) << byteToHex(*p++) << " "; 642 } 643 if (dumpSize < size) 644 { 645 (*_o) << "... "; 646 } 647 } 648 #endif 649 endl(OutputStream & o)650 static void endl(OutputStream &o) 651 { 652 OutputStream::endl(o); 653 } 654 655 OutputStream *_o; 656 OutputStream *_ostr; 657 std::ostringstream *_os; 658 659 TeeOutputStreams *stream_redirect; 660 }; 661 } 662 663 #endif // __CXXTEST__XMLFORMATTER_H 664 665