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 += "&quot;"; break;
179             case '\'': escStr += "&apos;"; break;
180             case '<':  escStr += "&lt;"; break;
181             case '>':  escStr += "&gt;"; break;
182             case '&':  escStr += "&amp;"; 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