diff --git a/include/internal/catch_xmlwriter.cpp b/include/internal/catch_xmlwriter.cpp index 5354efa7..b0d81ab7 100644 --- a/include/internal/catch_xmlwriter.cpp +++ b/include/internal/catch_xmlwriter.cpp @@ -10,6 +10,7 @@ #include "catch_enforce.h" #include +#include using uchar = unsigned char; @@ -51,8 +52,31 @@ namespace { os.flags(f); } + bool shouldNewline(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Newline)); + } + + bool shouldIndent(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Indent)); + } + } // anonymous namespace + XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs) + ); + } + + XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs) + ); + } + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) : m_str( str ), m_forWhat( forWhat ) @@ -157,13 +181,17 @@ namespace { return os; } - XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) - : m_writer( writer ) + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) + : m_writer( writer ), + m_fmt(fmt) {} XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept - : m_writer( other.m_writer ){ + : m_writer( other.m_writer ), + m_fmt(other.m_fmt) + { other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; } XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { if ( m_writer ) { @@ -171,17 +199,20 @@ namespace { } m_writer = other.m_writer; other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; return *this; } XmlWriter::ScopedElement::~ScopedElement() { - if( m_writer ) - m_writer->endElement(); + if (m_writer) { + m_writer->endElement(m_fmt); + } } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { - m_writer->writeText( text, indent ); + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) { + m_writer->writeText( text, fmt ); return *this; } @@ -191,37 +222,47 @@ namespace { } XmlWriter::~XmlWriter() { - while( !m_tags.empty() ) + while (!m_tags.empty()) { endElement(); + } + newlineIfNecessary(); } - XmlWriter& XmlWriter::startElement( std::string const& name ) { + XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { ensureTagClosed(); newlineIfNecessary(); - m_os << m_indent << '<' << name; + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; m_tags.push_back( name ); - m_indent += " "; m_tagIsOpen = true; + applyFormatting(fmt); return *this; } - XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { + ScopedElement scoped( this, fmt ); + startElement( name, fmt ); return scoped; } - XmlWriter& XmlWriter::endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); + XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); + if( m_tagIsOpen ) { m_os << "/>"; m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; } - else { - m_os << m_indent << ""; - } - m_os << std::endl; + m_os << std::flush; + applyFormatting(fmt); m_tags.pop_back(); return *this; } @@ -237,22 +278,26 @@ namespace { return *this; } - XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); - if( tagWasOpen && indent ) + if (tagWasOpen && shouldIndent(fmt)) { m_os << m_indent; + } m_os << XmlEncode( text ); - m_needsNewline = true; + applyFormatting(fmt); } return *this; } - XmlWriter& XmlWriter::writeComment( std::string const& text ) { + XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) { ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + applyFormatting(fmt); return *this; } @@ -268,11 +313,16 @@ namespace { void XmlWriter::ensureTagClosed() { if( m_tagIsOpen ) { - m_os << ">" << std::endl; + m_os << '>' << std::flush; + newlineIfNecessary(); m_tagIsOpen = false; } } + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } + void XmlWriter::writeDeclaration() { m_os << "\n"; } diff --git a/include/internal/catch_xmlwriter.h b/include/internal/catch_xmlwriter.h index c4b1c035..f551b233 100644 --- a/include/internal/catch_xmlwriter.h +++ b/include/internal/catch_xmlwriter.h @@ -14,6 +14,14 @@ #include namespace Catch { + enum class XmlFormatting { + None = 0x00, + Indent = 0x01, + Newline = 0x02, + }; + + XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs); + XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs); class XmlEncode { public: @@ -35,14 +43,14 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer ); + ScopedElement( XmlWriter* writer, XmlFormatting fmt ); ScopedElement( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept; ~ScopedElement(); - ScopedElement& writeText( std::string const& text, bool indent = true ); + ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent ); template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { @@ -52,6 +60,7 @@ namespace Catch { private: mutable XmlWriter* m_writer = nullptr; + XmlFormatting m_fmt; }; XmlWriter( std::ostream& os = Catch::cout() ); @@ -60,11 +69,11 @@ namespace Catch { XmlWriter( XmlWriter const& ) = delete; XmlWriter& operator=( XmlWriter const& ) = delete; - XmlWriter& startElement( std::string const& name ); + XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - ScopedElement scopedElement( std::string const& name ); + ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - XmlWriter& endElement(); + XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); @@ -77,9 +86,9 @@ namespace Catch { return writeAttribute( name, rss.str() ); } - XmlWriter& writeText( std::string const& text, bool indent = true ); + XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - XmlWriter& writeComment( std::string const& text ); + XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); void writeStylesheetRef( std::string const& url ); @@ -89,6 +98,8 @@ namespace Catch { private: + void applyFormatting(XmlFormatting fmt); + void writeDeclaration(); void newlineIfNecessary(); diff --git a/include/reporters/catch_reporter_junit.cpp b/include/reporters/catch_reporter_junit.cpp index f47f58f7..820b574f 100644 --- a/include/reporters/catch_reporter_junit.cpp +++ b/include/reporters/catch_reporter_junit.cpp @@ -147,8 +147,8 @@ namespace Catch { for( auto const& child : groupNode.children ) writeTestCase( *child ); - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); } void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { @@ -197,9 +197,9 @@ namespace Catch { writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); } for( auto const& childNode : sectionNode.childSections ) if( className.empty() ) @@ -271,7 +271,7 @@ namespace Catch { rss << msg.message << '\n'; rss << "at " << result.getSourceInfo(); - xml.writeText( rss.str(), false ); + xml.writeText( rss.str(), XmlFormatting::Newline ); } } diff --git a/include/reporters/catch_reporter_sonarqube.hpp b/include/reporters/catch_reporter_sonarqube.hpp index b860293c..bf7d9299 100644 --- a/include/reporters/catch_reporter_sonarqube.hpp +++ b/include/reporters/catch_reporter_sonarqube.hpp @@ -162,7 +162,7 @@ namespace Catch { textRss << msg.message << "\n"; textRss << "at " << result.getSourceInfo(); - xml.writeText(textRss.str(), false); + xml.writeText(textRss.str(), XmlFormatting::Newline); } } diff --git a/include/reporters/catch_reporter_xml.cpp b/include/reporters/catch_reporter_xml.cpp index 26ca8918..e110317c 100644 --- a/include/reporters/catch_reporter_xml.cpp +++ b/include/reporters/catch_reporter_xml.cpp @@ -193,9 +193,9 @@ namespace Catch { e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); m_xml.endElement(); }