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 v2.1 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 49 namespace CxxTest 50 { 51 class TeeOutputStreams 52 { 53 private: 54 class teebuffer : public std::basic_streambuf<char> 55 { 56 typedef std::basic_streambuf<char> streambuf_t; 57 public: teebuffer(streambuf_t * buf1,streambuf_t * buf2)58 teebuffer(streambuf_t * buf1, streambuf_t * buf2) 59 : buffer1(buf1), buffer2(buf2) 60 {} 61 overflow(int c)62 virtual int overflow(int c) 63 { 64 if (c == EOF) 65 return !EOF; 66 else 67 { 68 int const ans1 = buffer1->sputc(c); 69 int const ans2 = buffer2->sputc(c); 70 return ans1 == EOF || ans2 == EOF ? EOF : c; 71 } 72 } 73 sync()74 virtual int sync() 75 { 76 int ans1 = buffer1->pubsync(); 77 int ans2 = buffer2->pubsync(); 78 return ans1 || ans2 ? -1 : 0; 79 } 80 81 streambuf_t * buffer1; 82 streambuf_t * buffer2; 83 }; 84 85 public: TeeOutputStreams(std::ostream & _cout,std::ostream & _cerr)86 TeeOutputStreams(std::ostream& _cout, std::ostream& _cerr) 87 : out(), 88 err(), 89 orig_cout(_cout), 90 orig_cerr(_cerr), 91 tee_out(out.rdbuf(), _cout.rdbuf()), 92 tee_err(err.rdbuf(), _cerr.rdbuf()) 93 { 94 orig_cout.rdbuf(&tee_out); 95 orig_cerr.rdbuf(&tee_err); 96 } 97 ~TeeOutputStreams()98 ~TeeOutputStreams() 99 { 100 orig_cout.rdbuf(tee_out.buffer2); 101 orig_cerr.rdbuf(tee_err.buffer2); 102 } 103 104 std::stringstream out; 105 std::stringstream err; 106 107 private: 108 std::ostream& orig_cout; 109 std::ostream& orig_cerr; 110 teebuffer tee_out; 111 teebuffer tee_err; 112 }; 113 114 class ElementInfo 115 { 116 public: 117 std::string name; 118 std::stringstream value; 119 std::map<std::string,std::string> attribute; 120 ElementInfo()121 ElementInfo() 122 : name(), value(), attribute() 123 {} 124 ElementInfo(const ElementInfo & rhs)125 ElementInfo(const ElementInfo& rhs) 126 : name(rhs.name), value(rhs.value.str()), attribute(rhs.attribute) 127 {} 128 129 ElementInfo& operator=(const ElementInfo& rhs) 130 { 131 name = rhs.name; 132 value.str(rhs.value.str()); 133 attribute = rhs.attribute; 134 return *this; 135 } 136 137 template <class Type> add(const std::string & name_,Type & value_)138 void add(const std::string& name_, Type& value_) 139 { 140 std::ostringstream os; 141 os << value_; 142 attribute[name_] = os.str(); 143 } 144 write(OutputStream & os)145 void write(OutputStream& os) { 146 os << " <" << name.c_str() << " "; 147 std::map<std::string,std::string>::iterator curr=attribute.begin(); 148 std::map<std::string,std::string>::iterator end =attribute.end(); 149 while (curr != end) { 150 os << curr->first.c_str() 151 << "=\"" << curr->second.c_str() << "\" "; 152 curr++; 153 } 154 if (value.str().empty()) { 155 os << "/>"; 156 } 157 else { 158 os << ">" << escape(value.str()).c_str() 159 << "</" << name.c_str() << ">"; 160 } 161 os.endl(os); 162 } 163 escape(const std::string & str)164 std::string escape(const std::string& str) 165 { 166 std::string escStr = ""; 167 for(size_t i = 0; i < str.length(); i++) 168 { 169 switch(str[i]) 170 { 171 case '"': escStr += """; break; 172 case '\'': escStr += "'"; break; 173 case '<': escStr += "<"; break; 174 case '>': escStr += ">"; break; 175 case '&': escStr += "&"; break; 176 default: escStr += str[i]; break; 177 } 178 } 179 return escStr; 180 } 181 182 }; 183 184 class TestCaseInfo 185 { 186 public: 187 TestCaseInfo()188 TestCaseInfo() : fail(false), error(false), runtime(0.0) {} 189 std::string className; 190 std::string testName; 191 std::string line; 192 bool fail; 193 bool error; 194 double runtime; 195 std::list<ElementInfo> elements; 196 typedef std::list<ElementInfo>::iterator element_t; 197 std::string world; 198 add_element(const std::string & name)199 element_t add_element(const std::string& name) 200 { 201 element_t elt = elements.insert(elements.end(), ElementInfo()); 202 elt->name=name; 203 return elt; 204 } 205 update_element(const std::string & name)206 element_t update_element(const std::string& name) 207 { 208 element_t elt = elements.begin(); 209 while ( elt != elements.end() ) 210 { 211 if ( elt->name == name ) 212 return elt; 213 } 214 return add_element(name); 215 } 216 write(OutputStream & o)217 void write( OutputStream &o ) 218 { 219 o << " <testcase classname=\"" << className.c_str() 220 << "\" name=\"" << testName.c_str() 221 << "\" line=\"" << line.c_str() << "\""; 222 bool elts=false; 223 element_t curr = elements.begin(); 224 element_t end = elements.end(); 225 while (curr != end) { 226 if (!elts) { 227 o << ">"; 228 o.endl(o); 229 elts=true; 230 } 231 curr->write(o); 232 curr++; 233 } 234 if (elts) 235 o << " </testcase>"; 236 else 237 o << " />"; 238 o.endl(o); 239 } 240 241 }; 242 243 class XmlFormatter : public TestListener 244 { 245 public: XmlFormatter(OutputStream * o,OutputStream * ostr,std::ostringstream * os)246 XmlFormatter( OutputStream *o, OutputStream *ostr, std::ostringstream *os) 247 : _o(o), _ostr(ostr), _os(os), stream_redirect(NULL) 248 {} 249 250 std::list<TestCaseInfo> info; 251 std::list<TestCaseInfo>::iterator testcase; 252 typedef std::list<ElementInfo>::iterator element_t; 253 std::string classname; 254 int ntests; 255 int nfail; 256 int nerror; 257 double totaltime; 258 run()259 int run() 260 { 261 TestRunner::runAllTests( *this ); 262 return tracker().failedTests(); 263 } 264 enterWorld(const WorldDescription &)265 void enterWorld( const WorldDescription & /*desc*/ ) 266 { 267 ntests=0; 268 nfail=0; 269 nerror=0; 270 totaltime=0; 271 } 272 totalTests(OutputStream & o)273 static void totalTests( OutputStream &o ) 274 { 275 char s[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; 276 const WorldDescription &wd = tracker().world(); 277 o << wd.strTotalTests( s ) 278 << (wd.numTotalTests() == 1 ? " test" : " tests"); 279 } 280 enterSuite(const SuiteDescription & desc)281 void enterSuite( const SuiteDescription& desc ) 282 { 283 classname = desc.suiteName(); 284 // replace "::" namespace with java-style "." 285 size_t pos = 0; 286 while( (pos = classname.find("::", pos)) != 287 CXXTEST_STD(string::npos) ) 288 classname.replace(pos, 2, "."); 289 while ( ! classname.empty() && classname[0] == '.' ) 290 classname.erase(0,1); 291 292 //CXXTEST_STD(cout) << "HERE " << desc.file() << " " 293 // << classname << CXXTEST_STD(endl); 294 295 //classname=desc.suiteName(); 296 //(*_o) << "file=\"" << desc.file() << "\" "; 297 //(*_o) << "line=\"" << desc.line() << "\""; 298 //_o->flush(); 299 } 300 leaveSuite(const SuiteDescription &)301 void leaveSuite( const SuiteDescription & ) 302 { 303 std::list<TestCaseInfo>::iterator curr = info.begin(); 304 std::list<TestCaseInfo>::iterator end = info.end(); 305 while (curr != end) { 306 if (curr->fail) nfail++; 307 if (curr->error) nerror++; 308 totaltime += curr->runtime; 309 ntests++; 310 curr++; 311 } 312 curr = info.begin(); 313 end = info.end(); 314 while (curr != end) { 315 (*curr).write(*_ostr); 316 curr++; 317 } 318 info.clear(); 319 } 320 enterTest(const TestDescription & desc)321 void enterTest( const TestDescription & desc ) 322 { 323 testcase = info.insert(info.end(),TestCaseInfo()); 324 testcase->testName = desc.testName(); 325 testcase->className = classname; 326 std::ostringstream os; 327 os << desc.line(); 328 testcase->line = os.str(); 329 330 if ( stream_redirect ) 331 CXXTEST_STD(cerr) << "ERROR: The stream_redirect != NULL" 332 << CXXTEST_STD(endl); 333 334 stream_redirect = 335 new TeeOutputStreams(CXXTEST_STD(cout), CXXTEST_STD(cerr)); 336 } 337 leaveTest(const TestDescription &)338 void leaveTest( const TestDescription & ) 339 { 340 if ( stream_redirect != NULL ) 341 { 342 std::string out = stream_redirect->out.str(); 343 if ( ! out.empty() ) 344 { 345 // silently ignore the '.' 346 if ( out[0] != '.' || out.size() > 1 ) 347 testcase->add_element("system-out")->value << out; 348 } 349 if ( ! stream_redirect->err.str().empty() ) 350 testcase->add_element("system-err")->value << stream_redirect->err.str(); 351 352 delete stream_redirect; 353 stream_redirect = NULL; 354 } 355 } 356 leaveWorld(const WorldDescription & desc)357 void leaveWorld( const WorldDescription& desc ) 358 { 359 std::ostringstream os; 360 os << totaltime; 361 (*_o) << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl; 362 (*_o) << "<testsuite name=\"" << desc.worldName() << "\" "; 363 (*_o) << " tests=\"" << ntests 364 << "\" errors=\"" << nerror 365 << "\" failures=\"" << nfail 366 << "\" time=\"" << os.str().c_str() << "\" >"; 367 _o->endl(*_o); 368 (*_o) << _os->str().c_str(); 369 _os->clear(); 370 (*_o) << "</testsuite>" << endl; 371 _o->flush(); 372 } 373 trace(const char *,int line,const char * expression)374 void trace( const char* /*file*/, int line, const char *expression ) 375 { 376 element_t elt = testcase->add_element("trace"); 377 elt->add("line",line); 378 elt->value << expression; 379 } 380 warning(const char *,int line,const char * expression)381 void warning( const char* /*file*/, int line, const char *expression ) 382 { 383 element_t elt = testcase->add_element("warning"); 384 elt->add("line",line); 385 elt->value << expression; 386 } 387 failedTest(const char * file,int line,const char * expression)388 void failedTest( const char* file, int line, const char* expression ) 389 { 390 testFailure( file, line, "failure") << "Test failed: " << expression; 391 } 392 failedAssert(const char * file,int line,const char * expression)393 void failedAssert( const char *file, int line, const char *expression ) 394 { 395 testFailure( file, line, "failedAssert" ) 396 << "Assertion failed: " << expression; 397 } 398 failedAssertEquals(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)399 void failedAssertEquals( const char *file, int line, 400 const char* xStr, const char* yStr, 401 const char *x, const char *y ) 402 { 403 testFailure( file, line, "failedAssertEquals" ) 404 << "Error: Expected (" 405 << xStr << " == " << yStr << "), found (" 406 << x << " != " << y << ")"; 407 } 408 failedAssertSameData(const char * file,int line,const char * xStr,const char * yStr,const char * sizeStr,const void *,const void *,unsigned size)409 void failedAssertSameData( const char *file, int line, 410 const char *xStr, const char *yStr, const char *sizeStr, 411 const void* /*x*/, const void* /*y*/, unsigned size ) 412 { 413 testFailure( file, line, "failedAssertSameData") 414 << "Error: Expected " << sizeStr 415 << " (" << size << ") bytes to be equal at (" 416 << xStr << ") and (" << yStr << "), found"; 417 } 418 failedAssertSameFiles(const char * file,int line,const char *,const char *,const char * explanation)419 void failedAssertSameFiles( const char *file, int line, 420 const char *, const char *, 421 const char* explanation 422 ) 423 { 424 testFailure( file, line, "failedAssertSameFiles" ) 425 << "Error: " << explanation; 426 } 427 failedAssertDelta(const char * file,int line,const char * xStr,const char * yStr,const char * dStr,const char * x,const char * y,const char * d)428 void failedAssertDelta( const char *file, int line, 429 const char *xStr, const char *yStr, const char *dStr, 430 const char *x, const char *y, const char *d ) 431 { 432 testFailure( file, line, "failedAssertDelta" ) 433 << "Error: Expected (" 434 << xStr << " == " << yStr << ") up to " << dStr 435 << " (" << d << "), found (" 436 << x << " != " << y << ")"; 437 } 438 failedAssertDiffers(const char * file,int line,const char * xStr,const char * yStr,const char * value)439 void failedAssertDiffers( const char *file, int line, 440 const char *xStr, const char *yStr, 441 const char *value ) 442 { 443 testFailure( file, line, "failedAssertDiffers" ) 444 << "Error: Expected (" 445 << xStr << " != " << yStr << "), found (" 446 << value << ")"; 447 } 448 failedAssertLessThan(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)449 void failedAssertLessThan( const char *file, int line, 450 const char *xStr, const char *yStr, 451 const char *x, const char *y ) 452 { 453 testFailure( file, line, "failedAssertLessThan" ) 454 << "Error: Expected (" << 455 xStr << " < " << yStr << "), found (" << 456 x << " >= " << y << ")"; 457 } 458 failedAssertLessThanEquals(const char * file,int line,const char * xStr,const char * yStr,const char * x,const char * y)459 void failedAssertLessThanEquals( const char *file, int line, 460 const char *xStr, const char *yStr, 461 const char *x, const char *y ) 462 { 463 testFailure( file, line, "failedAssertLessThanEquals" ) 464 << "Error: Expected (" << 465 xStr << " <= " << yStr << "), found (" << 466 x << " > " << y << ")"; 467 } 468 failedAssertRelation(const char * file,int line,const char * relation,const char * xStr,const char * yStr,const char * x,const char * y)469 void failedAssertRelation( const char *file, int line, 470 const char *relation, const char *xStr, const char *yStr, 471 const char *x, const char *y ) 472 { 473 testFailure( file, line, "failedAssertRelation" ) 474 << "Error: Expected " << relation << "( " << 475 xStr << ", " << yStr << " ), found !" << relation 476 << "( " << x << ", " << y << " )"; 477 } 478 failedAssertPredicate(const char * file,int line,const char * predicate,const char * xStr,const char * x)479 void failedAssertPredicate( const char *file, int line, 480 const char *predicate, const char *xStr, const char *x ) 481 { 482 testFailure( file, line, "failedAssertPredicate" ) 483 << "Error: Expected " << predicate << "( " << 484 xStr << " ), found !" << predicate << "( " << x << " )"; 485 } 486 failedAssertThrows(const char * file,int line,const char * expression,const char * type,bool otherThrown)487 void failedAssertThrows( const char *file, int line, 488 const char *expression, const char *type, 489 bool otherThrown ) 490 { 491 testFailure( file, line, "failedAssertThrows" ) 492 << "Error: Expected (" << expression << ") to throw (" << 493 type << ") but it " 494 << (otherThrown ? "threw something else" : "didn't throw"); 495 } 496 failedAssertThrowsNot(const char * file,int line,const char * expression)497 void failedAssertThrowsNot( const char *file, int line, const char *expression ) 498 { 499 testFailure( file, line, "failedAssertThrowsNot" ) 500 << "Error: Expected (" << expression 501 << ") not to throw, but it did"; 502 } 503 504 protected: 505 outputStream()506 OutputStream *outputStream() const 507 { 508 return _o; 509 } 510 outputFileStream()511 OutputStream *outputFileStream() const 512 { 513 return _ostr; 514 } 515 516 private: 517 XmlFormatter( const XmlFormatter & ); 518 XmlFormatter &operator=( const XmlFormatter & ); 519 testFailure(const char * file,int line,const char * failureType)520 std::stringstream& testFailure( const char* file, int line, const char *failureType) 521 { 522 testcase->fail=true; 523 element_t elt = testcase->update_element("failure"); 524 if ( elt->value.str().empty() ) 525 { 526 elt->add("type",failureType); 527 elt->add("line",line); 528 elt->add("file",file); 529 } 530 else 531 elt->value << CXXTEST_STD(endl); 532 return elt->value; 533 //failedTest(file,line,message.c_str()); 534 } 535 536 #if 0 537 void attributeBinary( const char* name, const void *value, unsigned size ) 538 { 539 (*_o) << name; 540 (*_o) << "=\""; 541 dump(value, size); 542 (*_o) << "\" "; 543 } 544 545 void dump( const void *buffer, unsigned size ) 546 { 547 if (!buffer) return; 548 549 unsigned dumpSize = size; 550 if ( maxDumpSize() && dumpSize > maxDumpSize() ) 551 dumpSize = maxDumpSize(); 552 553 const unsigned char *p = (const unsigned char *)buffer; 554 for ( unsigned i = 0; i < dumpSize; ++ i ) 555 (*_o) << byteToHex( *p++ ) << " "; 556 if ( dumpSize < size ) 557 (*_o) << "... "; 558 } 559 #endif 560 endl(OutputStream & o)561 static void endl( OutputStream &o ) 562 { 563 OutputStream::endl( o ); 564 } 565 566 OutputStream *_o; 567 OutputStream *_ostr; 568 std::ostringstream *_os; 569 570 TeeOutputStreams *stream_redirect; 571 }; 572 } 573 574 #endif // __CXXTEST__XMLFORMATTER_H 575 576