From a189387f49f129a3554c47426a0dfe176c5c0781 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 6 Feb 2017 16:00:05 +0000 Subject: [PATCH] XmlWriter reverts to XML 1.0. Character encodings that are not valid in XML 1.0 are instead written using C-style escapes --- include/internal/catch_xmlwriter.hpp | 71 +++++++++++-------- .../Baselines/console.std.approved.txt | 2 +- .../Baselines/console.sw.approved.txt | 36 ++++++++-- .../SelfTest/Baselines/junit.sw.approved.txt | 10 +-- .../SelfTest/Baselines/xml.sw.approved.txt | 36 ++++++++-- projects/SelfTest/MiscTests.cpp | 18 +++-- 6 files changed, 121 insertions(+), 52 deletions(-) diff --git a/include/internal/catch_xmlwriter.hpp b/include/internal/catch_xmlwriter.hpp index e5fe1775..fb11d7d9 100644 --- a/include/internal/catch_xmlwriter.hpp +++ b/include/internal/catch_xmlwriter.hpp @@ -22,10 +22,12 @@ namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; + enum XmlVersion { _1_0, _1_1 }; - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) + XmlEncode( XmlVersion xmlVersion, std::string const& str, ForWhat forWhat = ForTextNodes ) : m_str( str ), - m_forWhat( forWhat ) + m_forWhat( forWhat ), + m_xmlVersion( xmlVersion ) {} void encodeTo( std::ostream& os ) const { @@ -57,8 +59,15 @@ namespace Catch { default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + if( m_xmlVersion == _1_0 ) + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + else + os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ) << ';'; + } else os << c; } @@ -73,6 +82,7 @@ namespace Catch { private: std::string m_str; ForWhat m_forWhat; + XmlVersion m_xmlVersion; }; class XmlWriter { @@ -112,20 +122,19 @@ namespace Catch { XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &Catch::cout() ) + m_os( Catch::cout() ), + m_xmlVersion( XmlEncode::_1_0 ) { - // We encode control characters, which requires - // XML 1.1 - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - *m_os << "\n"; + writeDeclaration(); } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &os ) + m_os( os ), + m_xmlVersion( XmlEncode::_1_0 ) { - *m_os << "\n"; + writeDeclaration(); } ~XmlWriter() { @@ -136,7 +145,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << '<' << name; + m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -153,11 +162,11 @@ namespace Catch { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { - stream() << "/>\n"; + m_os << "/>\n"; m_tagIsOpen = false; } else { - stream() << m_indent << "\n"; + m_os << m_indent << "\n"; } m_tags.pop_back(); return *this; @@ -165,12 +174,12 @@ namespace Catch { XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) - stream() << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + m_os << ' ' << name << "=\"" << XmlEncode( m_xmlVersion, attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -186,8 +195,8 @@ namespace Catch { bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); + m_os << m_indent; + m_os << XmlEncode( m_xmlVersion, text ); m_needsNewline = true; } return *this; @@ -195,39 +204,42 @@ namespace Catch { XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); - stream() << m_indent << ""; + m_os << m_indent << ""; m_needsNewline = true; return *this; } XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << '\n'; + m_os << '\n'; return *this; } - void setStream( std::ostream& os ) { - m_os = &os; - } - private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); - std::ostream& stream() { - return *m_os; + void writeDeclaration() { + switch( m_xmlVersion ) { + case XmlEncode::_1_0: + m_os << "\n"; + break; + case XmlEncode::_1_1: + m_os << "\n"; + break; + } } void ensureTagClosed() { if( m_tagIsOpen ) { - stream() << ">\n"; + m_os << ">\n"; m_tagIsOpen = false; } } void newlineIfNecessary() { if( m_needsNewline ) { - stream() << '\n'; + m_os << '\n'; m_needsNewline = false; } } @@ -236,7 +248,8 @@ namespace Catch { bool m_needsNewline; std::vector m_tags; std::string m_indent; - std::ostream* m_os; + std::ostream& m_os; + XmlEncode::XmlVersion m_xmlVersion; }; } diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index ea2e1008..b5f4a482 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -830,5 +830,5 @@ with expansion: =============================================================================== test cases: 157 | 113 passed | 42 failed | 2 failed as expected -assertions: 913 | 817 passed | 78 failed | 18 failed as expected +assertions: 915 | 819 passed | 78 failed | 18 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 2ea4dd0b..fb34373b 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -7982,27 +7982,53 @@ with expansion: ------------------------------------------------------------------------------- XmlEncode - string with control char (1) + string with control char (1) (XML 1.0) ------------------------------------------------------------------------------- MiscTests.cpp: ............................................................................... MiscTests.cpp:: PASSED: - REQUIRE( encode( "[\x01]" ) == "[]" ) + REQUIRE( encode( "[\x01]" ) == "[\\x01]" ) +with expansion: + "[\x01]" == "[\x01]" + +------------------------------------------------------------------------------- +XmlEncode + string with control char (1) (XMl 1.1) +------------------------------------------------------------------------------- +MiscTests.cpp: +............................................................................... + +MiscTests.cpp:: +PASSED: + REQUIRE( encode( "[\x01]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[]" ) with expansion: "[]" == "[]" ------------------------------------------------------------------------------- XmlEncode - string with control char (x7F) + string with control char (x7F) (XML 1.0) ------------------------------------------------------------------------------- MiscTests.cpp: ............................................................................... MiscTests.cpp:: PASSED: - REQUIRE( encode( "[\x7F]" ) == "[]" ) + REQUIRE( encode( "[\x7F]" ) == "[\\x7F]" ) +with expansion: + "[\x7F]" == "[\x7F]" + +------------------------------------------------------------------------------- +XmlEncode + string with control char (x7F) (XML 1.1) +------------------------------------------------------------------------------- +MiscTests.cpp: +............................................................................... + +MiscTests.cpp:: +PASSED: + REQUIRE( encode( "[\x7F]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[]" ) with expansion: "[]" == "[]" @@ -9026,5 +9052,5 @@ PASSED: =============================================================================== test cases: 157 | 112 passed | 43 failed | 2 failed as expected -assertions: 915 | 817 passed | 80 failed | 18 failed as expected +assertions: 917 | 819 passed | 80 failed | 18 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 55ee56c9..e7e650de 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,6 +1,6 @@ - + - + @@ -538,8 +538,10 @@ ExceptionTests.cpp: - - + + + + diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index dc192152..137f55e2 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -1,4 +1,4 @@ - + @@ -8445,10 +8445,21 @@ there" -
+
- encode( "[\x01]" ) == "[&#x01;]" + encode( "[\x01]" ) == "[\\x01]" + + + "[\x01]" == "[\x01]" + + + +
+
+ + + encode( "[\x01]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[&#x01;]" "[&#x01;]" == "[&#x01;]" @@ -8456,10 +8467,21 @@ there"
-
+
- encode( "[\x7F]" ) == "[&#x7F;]" + encode( "[\x7F]" ) == "[\\x7F]" + + + "[\x7F]" == "[\x7F]" + + + +
+
+ + + encode( "[\x7F]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[&#x7F;]" "[&#x7F;]" == "[&#x7F;]" @@ -9503,7 +9525,7 @@ there"
- + - + diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index 5de2ca48..211ba7d7 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -429,9 +429,9 @@ TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) { CHECK( result == "\"wide load\"" ); } -inline std::string encode( std::string const& str, Catch::XmlEncode::ForWhat forWhat = Catch::XmlEncode::ForTextNodes ) { +inline std::string encode( std::string const& str, Catch::XmlEncode::ForWhat forWhat = Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::XmlVersion version = Catch::XmlEncode::_1_0 ) { std::ostringstream oss; - oss << Catch::XmlEncode( str, forWhat ); + oss << Catch::XmlEncode( version, str, forWhat ); return oss.str(); } @@ -457,11 +457,17 @@ TEST_CASE( "XmlEncode" ) { REQUIRE( encode( stringWithQuotes ) == stringWithQuotes ); REQUIRE( encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) == "don't "quote" me on that" ); } - SECTION( "string with control char (1)" ) { - REQUIRE( encode( "[\x01]" ) == "[]" ); + SECTION( "string with control char (1) (XML 1.0)" ) { + REQUIRE( encode( "[\x01]" ) == "[\\x01]" ); } - SECTION( "string with control char (x7F)" ) { - REQUIRE( encode( "[\x7F]" ) == "[]" ); + SECTION( "string with control char (1) (XMl 1.1)" ) { + REQUIRE( encode( "[\x01]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[]" ); + } + SECTION( "string with control char (x7F) (XML 1.0)" ) { + REQUIRE( encode( "[\x7F]" ) == "[\\x7F]" ); + } + SECTION( "string with control char (x7F) (XML 1.1)" ) { + REQUIRE( encode( "[\x7F]", Catch::XmlEncode::ForTextNodes, Catch::XmlEncode::_1_1 ) == "[]" ); } }