@@ -23,13 +23,22 @@ XMLOutput::~XMLOutput() noexcept
2323 output.flush ();
2424}
2525
26- static std::string escapeQuotes (std::string text)
26+ static std::string escapeXML (std::string text)
2727{
2828 std::size_t pos = 0 ;
29- while ((pos = text.find ( ' " ' , pos)) != std::string::npos)
29+ while ((pos = text.find_first_of ( " \" '<>& " , pos)) != std::string::npos)
3030 {
31- text.replace (pos, 1 , " \\\" " );
32- pos += 2 ;
31+ if (text[pos] == ' "' )
32+ text.replace (pos, 1 , " "" );
33+ else if (text[pos] == ' \' ' )
34+ text.replace (pos, 1 , " '" );
35+ else if (text[pos] == ' <' )
36+ text.replace (pos, 1 , " <" );
37+ else if (text[pos] == ' >' )
38+ text.replace (pos, 1 , " >" );
39+ else if (text[pos] == ' &' )
40+ text.replace (pos, 1 , " &" );
41+ pos += 4 ;
3342 }
3443 return text;
3544}
@@ -42,7 +51,7 @@ void XMLOutput::finishSuite(const std::string& suiteName, unsigned int numTests,
4251 std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(totalDuration);
4352 std::chrono::microseconds remainder = totalDuration - seconds;
4453 unsigned numErrors = std::count_if (currentSuite->methods .begin (), currentSuite->methods .end (), [] (const TestMethodInfo& method) { return !method.exceptionMessage .empty (); });
45- output << " \t <testsuite name=\" " << suiteName << " \" tests=\" " << numTests << " \" failures=\" "
54+ output << " \t <testsuite name=\" " << escapeXML ( suiteName) << " \" tests=\" " << numTests << " \" failures=\" "
4655 << (numTests - numPositiveTests - numErrors) << " \" errors=\" " << numErrors << " \" time=\" "
4756 << seconds.count () << ' .' << std::setfill (' 0' ) << std::setw (3 ) << remainder.count () << " \" timestamp=\" "
4857 << std::put_time (gmtime (&now), " %FT%T" ) << " \" >\n " ;
@@ -52,24 +61,22 @@ void XMLOutput::finishSuite(const std::string& suiteName, unsigned int numTests,
5261 std::string name = stripMethodName (method.methodName );
5362 if (!method.argString .empty ())
5463 name.append (" (" + method.argString + " )" );
55- output << " \t\t <testcase classname=\" " << suiteName << " \" name=\" " << escapeQuotes (name) << " \" >\n " ;
64+ output << " \t\t <testcase classname=\" " << escapeXML ( suiteName) << " \" name=\" " << escapeXML (name) << " \" >\n " ;
5665 if (!method.exceptionMessage .empty ())
57- output << " \t\t\t <error message=\" " << escapeQuotes (method.exceptionMessage ) << " \" type=\"\" />\n " ;
66+ output << " \t\t\t <error message=\" " << escapeXML (method.exceptionMessage ) << " \" type=\"\" />\n " ;
5867 else if (method.failedAssertions .empty () && method.passedAssertions .empty ())
5968 output << " \t\t\t <skipped message=\" Test case has no assertions\" type=\"\" />\n " ;
6069 else if (!method.failedAssertions .empty ())
6170 {
62- output << " \t\t\t <failure message=\" " << method.failedAssertions .size () << " assertions failed\" type=\"\" >\n " ;
6371 for (const Assertion& assertion : method.failedAssertions )
6472 {
65- output << " \t\t\t\t Failure: " << assertion.errorMessage << ' \n ' ;
66- output << " \t\t\t\t File: " << Private::getFileName (assertion.file ) << ' \n ' ;
73+ output << " \t\t\t <failure message= \" " << ( assertion.errorMessage . empty () ? " Failure " : escapeXML (assertion. errorMessage )) << " \" type= \"\" > \n " ;
74+ output << " \t\t\t\t File: " << escapeXML ( Private::getFileName (assertion.file ) ) << ' \n ' ;
6775 output << " \t\t\t\t Line: " << assertion.lineNumber << ' \n ' ;
6876 if (!assertion.userMessage .empty ())
69- output << " \t\t\t\t Message: " << assertion.userMessage << ' \n ' ;
70- output << ' \n ' ;
77+ output << " \t\t\t\t Message: " << escapeXML ( assertion.userMessage ) << ' \n ' ;
78+ output << " \t\t\t </failure> \n " ;
7179 }
72- output << " \t\t\t </failure>\n " ;
7380 }
7481 output << " \t\t </testcase>\n " ;
7582 }
0 commit comments