libtest: improve output format; makes eyes hurt less
[awesomized/libmemcached] / libtest / formatter.cc
index e0a347a05e862607dad7086cee6cb99f27b07105..443256b617ecf66ddeb8c98144f8710f8e629c2e 100644 (file)
  *
  */
 
-#include "mem_config.h"
+#include "libtest/yatlcon.h"
 
 #include <libtest/common.h>
 
-#include <iostream>
+#include <algorithm>
 #include <fstream>
-  
+#include <iostream>
+#include <ostream>
+
 namespace libtest {
 
+std::string& escape4XML(std::string const& arg, std::string& escaped_string)
+{
+  escaped_string.clear();
+
+  escaped_string+= '"';
+  for (std::string::const_iterator x= arg.begin(), end= arg.end(); x != end; ++x)
+  {
+    unsigned char c= *x;
+    if (c == '&')
+    {
+      escaped_string+= "&amp;";
+    }
+    else if (c == '>')
+    {
+      escaped_string+= "&gt;";
+    }
+    else if (c == '<')
+    {
+      escaped_string+= "&lt;";
+    }
+    else if (c == '\'')
+    {
+      escaped_string+= "&apos;";  break;
+    }
+    else if (c == '"')
+    {
+      escaped_string+= "&quot;";
+    }
+    else if (c == ' ')
+    {
+      escaped_string+= ' ';
+    }
+    else if (isalnum(c))
+    {
+      escaped_string+= c;
+    }
+    else 
+    {
+      char const* const hexdig= "0123456789ABCDEF";
+      escaped_string+= "&#x";
+      escaped_string+= hexdig[c >> 4];
+      escaped_string+= hexdig[c & 0xF];
+      escaped_string+= ';';
+    }
+  }
+  escaped_string+= '"';
+
+  return escaped_string;
+}
+
 class TestCase {
 public:
   TestCase(const std::string& arg):
@@ -97,10 +149,8 @@ Formatter::Formatter(const std::string& frame_name, const std::string& arg)
 
 Formatter::~Formatter()
 {
-  for (TestCases::iterator iter= _testcases.begin(); iter != _testcases.end(); ++iter)
-  {
-    delete *iter;
-  }
+  std::for_each(_testcases.begin(), _testcases.end(), DeleteFromVector());
+  _testcases.clear();
 }
 
 TestCase* Formatter::current()
@@ -110,8 +160,14 @@ TestCase* Formatter::current()
 
 void Formatter::skipped()
 {
+  assert(current());
   current()->result(TEST_SKIPPED);
-  Out << name() << "." << current()->name() <<  "\t\t\t\t\t" << "[ " << test_strerror(current()->result()) << " ]";
+
+  Out
+    << "[ " << test_strerror(current()->result()) << " ]"
+    << "\t\t"
+    << name() << "." << current()->name()
+    ;
 
   reset();
 }
@@ -121,7 +177,11 @@ void Formatter::failed()
   assert(current());
   current()->result(TEST_FAILURE);
 
-  Out << name() << "." << current()->name() <<  "\t\t\t\t\t" << "[ " << test_strerror(current()->result()) << " ]";
+  Out
+    << "[ " << test_strerror(current()->result()) << " ]"
+    << "\t\t"
+    << name() << "." << current()->name()
+    ;
 
   reset();
 }
@@ -131,49 +191,63 @@ void Formatter::success(const libtest::Timer& timer_)
   assert(current());
   current()->result(TEST_SUCCESS, timer_);
 
-  Out << name() << "."
-    << current()->name()
-    <<  "\t\t\t\t\t" 
-    << current()->timer() 
-    << " [ " << test_strerror(current()->result()) << " ]";
+  Out
+    << "[ " << test_strerror(current()->result()) << " ]"
+    << "\t"
+    << current()->timer()
+    << "\t"
+    << name() << "." << current()->name()
+    ;
 
   reset();
 }
 
 void Formatter::xml(libtest::Framework& framework_, std::ofstream& output)
 {
-  output << "<testsuites name=\"" << framework_.name() << "\">" << std::endl;
+  std::string escaped_string;
+
+  output << "<testsuites name=" 
+    << escape4XML(framework_.name(), escaped_string) << ">" << std::endl;
+
   for (Suites::iterator framework_iter= framework_.suites().begin();
        framework_iter != framework_.suites().end();
        ++framework_iter)
   {
-    output << "\t<testsuite name=\"" << (*framework_iter)->name() << "\"  classname=\"\" package=\"\">" << std::endl;
+    output << "\t<testsuite name=" 
+      << escape4XML((*framework_iter)->name(), escaped_string)
+#if 0
+      << "  classname=\"\" package=\"\"" 
+#endif
+      << ">" << std::endl;
 
     for (TestCases::iterator case_iter= (*framework_iter)->formatter()->testcases().begin();
          case_iter != (*framework_iter)->formatter()->testcases().end();
          ++case_iter)
     {
-      output << "\t\t<testcase name=\"
-        << (*case_iter)->name() 
-        << "\" time=\"" 
+      output << "\t\t<testcase name=" 
+        << escape4XML((*case_iter)->name(), escaped_string)
+        << " time=\"" 
         << (*case_iter)->timer().elapsed_milliseconds() 
-        << "\">" 
-        << std::endl;
+        << "\""; 
 
       switch ((*case_iter)->result())
       {
         case TEST_SKIPPED:
+        output << ">" << std::endl;
         output << "\t\t <skipped/>" << std::endl;
+        output << "\t\t</testcase>" << std::endl;
         break;
 
         case TEST_FAILURE:
+        output << ">" << std::endl;
         output << "\t\t <failure message=\"\" type=\"\"/>"<< std::endl;
+        output << "\t\t</testcase>" << std::endl;
         break;
 
         case TEST_SUCCESS:
+        output << "/>" << std::endl;
         break;
       }
-      output << "\t\t</testcase>" << std::endl;
     }
     output << "\t</testsuite>" << std::endl;
   }
@@ -185,6 +259,14 @@ void Formatter::push_testcase(const std::string& arg)
   assert(_suite_name.empty() == false);
   TestCase* _current_testcase= new TestCase(arg);
   _testcases.push_back(_current_testcase);
+
+  assert(current());
+
+  Echo
+    << "\t\t\t"
+    << name() << "." << current()->name()
+    << "... \r"
+    ;
 }
 
 void Formatter::reset()