mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 03:43:28 +01:00
Refactored XMLWriter to provide finer-grained control over formatting
This commit is contained in:
parent
74e0e737a6
commit
3136c4fb6a
@ -10,6 +10,7 @@
|
||||
#include "catch_enforce.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <type_traits>
|
||||
|
||||
using uchar = unsigned char;
|
||||
|
||||
@ -51,8 +52,31 @@ namespace {
|
||||
os.flags(f);
|
||||
}
|
||||
|
||||
bool shouldNewline(XmlFormatting fmt) {
|
||||
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
|
||||
}
|
||||
|
||||
bool shouldIndent(XmlFormatting fmt) {
|
||||
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
|
||||
return static_cast<XmlFormatting>(
|
||||
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
|
||||
static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
|
||||
);
|
||||
}
|
||||
|
||||
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
|
||||
return static_cast<XmlFormatting>(
|
||||
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
|
||||
static_cast<std::underlying_type<XmlFormatting>::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 << "</" << m_tags.back() << ">";
|
||||
}
|
||||
else {
|
||||
m_os << m_indent << "</" << m_tags.back() << ">";
|
||||
}
|
||||
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 << "<!--" << text << "-->";
|
||||
m_needsNewline = true;
|
||||
if (shouldIndent(fmt)) {
|
||||
m_os << m_indent;
|
||||
}
|
||||
m_os << "<!--" << text << "-->";
|
||||
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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
}
|
||||
|
@ -14,6 +14,14 @@
|
||||
#include <vector>
|
||||
|
||||
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<typename T>
|
||||
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();
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user