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 += "&quot;"; break;
172                     case '\'': escStr += "&apos;"; break;
173                     case '<':  escStr += "&lt;"; break;
174                     case '>':  escStr += "&gt;"; break;
175                     case '&':  escStr += "&amp;"; 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