[libxml++] Introduce xmlpp::wrapped_exception



commit 128615309454dc75ffcb9eaf83fe1188e35b5dfe
Author: Daniel Trebbien <dtrebbien gmail com>
Date:   Thu Aug 27 16:42:45 2015 +0200

    Introduce xmlpp::wrapped_exception
    
    This is an internal class which is used by SaxParser and Validator to
    save the exception object thrown by a handler method when the exception
    does not derive from xmlpp::exception (e.g. std::exception). The Raise()
    method of xmlpp::wrapped_exception calls std::rethrow_exception() to
    rethrow the exception object thrown by the handler method.
    
    Catching any exception object thrown by a handler method is important in
    ensuring that we are able to reset the internal state, and, in the case
    of SaxParser::parse(), that we restore the old _xmlSAXHandler pointer so
    that we do not double-free the _xmlSAXHandler object held by SaxParser.
    
    Fixes Bug 753570 - “double free or corruption” if a std::exception is thrown
    https://bugzilla.gnome.org/show_bug.cgi?id=753570

 .gitignore                                         |    8 +
 Makefile.am                                        |    3 +-
 configure.ac                                       |    1 +
 libxml++/exceptions/wrapped_exception.cc           |   42 +++
 libxml++/exceptions/wrapped_exception.h            |   43 +++
 libxml++/parsers/parser.cc                         |    5 +
 libxml++/parsers/saxparser.cc                      |   55 ++++-
 libxml++/validators/validator.cc                   |    9 +
 tests/Makefile.am                                  |   30 ++
 .../main.cc                                        |   80 +++++
 tests/saxparser_parse_double_free/main.cc          |  321 ++++++++++++++++++++
 .../main.cc                                        |   76 +++++
 12 files changed, 671 insertions(+), 2 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 270b5bf..b12c415 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,6 +68,7 @@ stamp-h?
 /examples/test-suite.log
 
 # macros
+/macros/compile
 /macros/compile-binding.am
 /macros/config.guess
 /macros/config.sub
@@ -83,3 +84,10 @@ stamp-h?
 /macros/ltversion.m4
 /macros/lt~obsolete.m4
 /macros/missing
+/macros/test-driver
+
+# tests
+/tests/*/test
+/tests/*/test.log
+/tests/*/test.trs
+/tests/test-suite.log
diff --git a/Makefile.am b/Makefile.am
index 69f5336..4483936 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . examples
+SUBDIRS = . examples tests
 
 ACLOCAL_AMFLAGS = -I macros ${ACLOCAL_FLAGS}
 DISTCHECK_CONFIGURE_FLAGS = --enable-warnings=fatal
@@ -82,6 +82,7 @@ cc_sources = libxml++/attribute.cc \
        libxml++/exceptions/parse_error.cc \
        libxml++/exceptions/validity_error.cc \
        libxml++/exceptions/internal_error.cc \
+       libxml++/exceptions/wrapped_exception.cc \
        libxml++/io/istreamparserinputbuffer.cc \
        libxml++/io/outputbuffer.cc \
        libxml++/io/ostreamoutputbuffer.cc \
diff --git a/configure.ac b/configure.ac
index 42379fd..8ecc05d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,7 @@ AC_DEFINE([LIBXMLCPP_EXCEPTIONS_ENABLED],[1], [This is always set. This is only
 
 AC_CONFIG_FILES([Makefile
         examples/Makefile
+        tests/Makefile
         docs/reference/Doxyfile
         MSVC_Net2005/libxml++/libxml++.rc
         MSVC_Net2008/libxml++/libxml++.rc
diff --git a/libxml++/exceptions/wrapped_exception.cc b/libxml++/exceptions/wrapped_exception.cc
new file mode 100644
index 0000000..8799e74
--- /dev/null
+++ b/libxml++/exceptions/wrapped_exception.cc
@@ -0,0 +1,42 @@
+/* Copyright (C) 2015  The libxml++ development team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "wrapped_exception.h"
+
+namespace xmlpp
+{
+
+wrapped_exception::wrapped_exception(std::exception_ptr exception_ptr)
+  : exception("Wrapped exception"), exception_ptr_(exception_ptr)
+{
+}
+
+wrapped_exception::~wrapped_exception() noexcept
+{
+}
+
+void wrapped_exception::Raise() const
+{
+  std::rethrow_exception(exception_ptr_);
+}
+
+exception* wrapped_exception::Clone() const
+{
+  return new wrapped_exception(exception_ptr_);
+}
+
+} // namespace xmlpp
diff --git a/libxml++/exceptions/wrapped_exception.h b/libxml++/exceptions/wrapped_exception.h
new file mode 100644
index 0000000..0653842
--- /dev/null
+++ b/libxml++/exceptions/wrapped_exception.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2015  The libxml++ development team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __LIBXMLPP_WRAPPED_EXCEPTION_H
+#define __LIBXMLPP_WRAPPED_EXCEPTION_H
+
+#include <exception>
+
+#include <libxml++/exceptions/exception.h>
+
+namespace xmlpp
+{
+
+class wrapped_exception : public exception
+{
+public:
+  explicit wrapped_exception(std::exception_ptr exception_ptr);
+  ~wrapped_exception() noexcept override;
+
+  void Raise() const override;
+  exception* Clone() const override;
+
+private:
+  std::exception_ptr exception_ptr_;
+};
+
+} // namespace xmlpp
+
+#endif // __LIBXMLPP_WRAPPED_EXCEPTION_H
diff --git a/libxml++/parsers/parser.cc b/libxml++/parsers/parser.cc
index 34ad6e4..bd3865c 100644
--- a/libxml++/parsers/parser.cc
+++ b/libxml++/parsers/parser.cc
@@ -8,6 +8,7 @@
 // because it temporarily undefines G_DISABLE_DEPRECATED while it includes glib.h.
 #include <glibmm/threads.h> // For Glib::Threads::Mutex. Needed until the next API/ABI break.
 
+#include "libxml++/exceptions/wrapped_exception.h"
 #include "libxml++/parsers/parser.h"
 
 #include <libxml/parser.h>
@@ -350,6 +351,10 @@ void Parser::callback_error_or_warning(MsgType msg_type, void* ctx,
       {
         parser->handleException(e);
       }
+      catch(...)
+      {
+        parser->handleException(wrapped_exception(std::current_exception()));
+      }
     }
   }
 }
diff --git a/libxml++/parsers/saxparser.cc b/libxml++/parsers/saxparser.cc
index e89f1df..17531b2 100644
--- a/libxml++/parsers/saxparser.cc
+++ b/libxml++/parsers/saxparser.cc
@@ -7,6 +7,7 @@
  * 2002/01/21 Valentin Rusu - added CDATA handlers
  */
 
+#include "libxml++/exceptions/wrapped_exception.h"
 #include "libxml++/parsers/saxparser.h"
 #include "libxml++/nodes/element.h"
 #include "libxml++/keepblanks.h"
@@ -384,7 +385,11 @@ xmlEntityPtr SaxParserCallback::get_entity(void* context, const xmlChar* name)
   {
     parser->handleException(e);
   }
-  
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
+
   return result;
 }
 
@@ -406,6 +411,10 @@ void SaxParserCallback::entity_decl(void* context, const xmlChar* name, int type
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::start_document(void* context)
@@ -421,6 +430,10 @@ void SaxParserCallback::start_document(void* context)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::end_document(void* context)
@@ -439,6 +452,10 @@ void SaxParserCallback::end_document(void* context)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::start_element(void* context,
@@ -463,6 +480,10 @@ void SaxParserCallback::start_element(void* context,
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::end_element(void* context, const xmlChar* name)
@@ -478,6 +499,10 @@ void SaxParserCallback::end_element(void* context, const xmlChar* name)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::characters(void * context, const xmlChar* ch, int len)
@@ -499,6 +524,10 @@ void SaxParserCallback::characters(void * context, const xmlChar* ch, int len)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::comment(void* context, const xmlChar* value)
@@ -514,6 +543,10 @@ void SaxParserCallback::comment(void* context, const xmlChar* value)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::warning(void* context, const char* fmt, ...)
@@ -536,6 +569,10 @@ void SaxParserCallback::warning(void* context, const char* fmt, ...)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::error(void* context, const char* fmt, ...)
@@ -561,6 +598,10 @@ void SaxParserCallback::error(void* context, const char* fmt, ...)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::fatal_error(void* context, const char* fmt, ...)
@@ -583,6 +624,10 @@ void SaxParserCallback::fatal_error(void* context, const char* fmt, ...)
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::cdata_block(void* context, const xmlChar* value, int len)
@@ -603,6 +648,10 @@ void SaxParserCallback::cdata_block(void* context, const xmlChar* value, int len
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 void SaxParserCallback::internal_subset(void* context, const xmlChar* name,
@@ -622,6 +671,10 @@ void SaxParserCallback::internal_subset(void* context, const xmlChar* name,
   {
     parser->handleException(e);
   }
+  catch(...)
+  {
+    parser->handleException(wrapped_exception(std::current_exception()));
+  }
 }
 
 } // namespace xmlpp
diff --git a/libxml++/validators/validator.cc b/libxml++/validators/validator.cc
index 3323e19..220d459 100644
--- a/libxml++/validators/validator.cc
+++ b/libxml++/validators/validator.cc
@@ -5,6 +5,7 @@
  * included with libxml++ as the file COPYING.
  */
 
+#include "libxml++/exceptions/wrapped_exception.h"
 #include "libxml++/validators/validator.h"
 
 #include <libxml/parser.h>
@@ -114,6 +115,10 @@ void Validator::callback_validity_error(void* valid_, const char* msg, ...)
     {
       validator->handleException(e);
     }
+    catch(...)
+    {
+      validator->handleException(wrapped_exception(std::current_exception()));
+    }
   }
 }
 
@@ -139,6 +144,10 @@ void Validator::callback_validity_warning(void* valid_, const char* msg, ...)
     {
       validator->handleException(e);
     }
+    catch(...)
+    {
+      validator->handleException(wrapped_exception(std::current_exception()));
+    }
   }
 }
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..96bfe23
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,30 @@
+## Copyright (C) 2015  The libxml++ development team
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I. $(LIBXMLXX_CFLAGS)
+AM_CXXFLAGS = $(LIBXMLXX_WXXFLAGS)
+LDADD = $(top_builddir)/libxml++/libxml++-$(LIBXMLXX_API_VERSION).la $(LIBXMLXX_LIBS)
+
+check_PROGRAMS = \
+       saxparser_chunk_parsing_inconsistent_state/test \
+       saxparser_parse_double_free/test \
+       saxparser_parse_stream_inconsistent_state/test
+
+TESTS = $(check_PROGRAMS)
+
+saxparser_chunk_parsing_inconsistent_state_test_SOURCES = saxparser_chunk_parsing_inconsistent_state/main.cc
+saxparser_parse_double_free_test_SOURCES = saxparser_parse_double_free/main.cc
+saxparser_parse_stream_inconsistent_state_test_SOURCES = saxparser_parse_stream_inconsistent_state/main.cc
diff --git a/tests/saxparser_chunk_parsing_inconsistent_state/main.cc 
b/tests/saxparser_chunk_parsing_inconsistent_state/main.cc
new file mode 100644
index 0000000..7e5a636
--- /dev/null
+++ b/tests/saxparser_chunk_parsing_inconsistent_state/main.cc
@@ -0,0 +1,80 @@
+/* Copyright (C) 2015  The libxml++ development team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cstdlib>
+#include <glibmm.h>
+#include <sstream>
+#include <stdexcept>
+
+#include <libxml++/libxml++.h>
+
+class MySaxParser : public xmlpp::SaxParser
+{
+protected:
+  void on_start_document() override
+  {
+    throw std::runtime_error("some custom runtime exception");
+  }
+  void on_error(const Glib::ustring& text) override
+  {
+    throw std::runtime_error("on_error() called");
+  }
+};
+
+int main()
+{
+  Glib::init();
+
+  {
+    MySaxParser parser;
+
+    bool exceptionThrown = false;
+    try
+    {
+      parser.parse_chunk("<?");
+      parser.finish_chunk_parsing();
+    }
+    catch(const std::runtime_error& e)
+    {
+      exceptionThrown = true;
+      g_assert_cmpstr(e.what(), ==, "on_error() called");
+    }
+    g_assert_true(exceptionThrown);
+
+    // Try parsing a stream now.
+    exceptionThrown = false;
+    try
+    {
+      std::stringstream ss("<root></root>");
+      parser.parse_stream(ss);
+    }
+    catch(const xmlpp::parse_error& e)
+    {
+      // An "Attempt to start a second parse while a parse is in progress." parse
+      // error should not have been thrown.
+      g_assert_not_reached();
+    }
+    catch(const std::runtime_error& e)
+    {
+      exceptionThrown = true;
+      g_assert_cmpstr(e.what(), ==, "some custom runtime exception");
+    }
+    g_assert_true(exceptionThrown);
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/saxparser_parse_double_free/main.cc b/tests/saxparser_parse_double_free/main.cc
new file mode 100644
index 0000000..67dacd8
--- /dev/null
+++ b/tests/saxparser_parse_double_free/main.cc
@@ -0,0 +1,321 @@
+/* Copyright (C) 2015  The libxml++ development team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cstdlib>
+#include <glibmm.h>
+#include <stdexcept>
+
+#include <libxml++/libxml++.h>
+
+
+class OnCdataBlockTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_cdata_block(const Glib::ustring& text) override
+  {
+    g_assert_cmpstr(text.c_str(), ==, "some CDATA");
+    throw std::runtime_error("on_cdata_block runtime exception");
+  }
+};
+
+void test_on_cdata_block()
+{
+  OnCdataBlockTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root><![CDATA[some CDATA]]></root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_cdata_block runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnCharactersTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_characters(const Glib::ustring& characters) override
+  {
+    g_assert_cmpstr(characters.c_str(), ==, "abc");
+    throw std::runtime_error("on_characters runtime exception");
+  }
+};
+
+void test_on_characters()
+{
+  OnCharactersTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root>abc</root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_characters runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnCommentTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_comment(const Glib::ustring& text) override
+  {
+    g_assert_cmpstr(text.c_str(), ==, "a comment");
+    throw std::runtime_error("on_comment runtime exception");
+  }
+};
+
+void test_on_comment()
+{
+  OnCommentTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root><!--a comment--></root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_comment runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnEndDocumentTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_end_document() override
+  {
+    throw std::runtime_error("on_end_document runtime exception");
+  }
+};
+
+void test_on_end_document()
+{
+  OnEndDocumentTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root></root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_end_document runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnEndElementTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_end_element(const Glib::ustring& name) override
+  {
+    g_assert_cmpstr(name.c_str(), ==, "a:root");
+    throw std::runtime_error("on_end_element runtime exception");
+  }
+};
+
+void test_on_end_element()
+{
+  OnEndElementTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<a:root xmlns:a=\"urn:test\"></a:root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_end_element runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnEntityDeclarationTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_entity_declaration(const Glib::ustring& name, xmlpp::XmlEntityType type, const Glib::ustring& 
publicId, const Glib::ustring& systemId, const Glib::ustring& content) override
+  {
+    g_assert_cmpstr(name.c_str(), ==, "number");
+    g_assert_cmpstr(content.c_str(), ==, "42");
+    throw std::runtime_error("on_entity_declaration runtime exception");
+  }
+};
+
+void test_on_entity_declaration()
+{
+  OnEntityDeclarationTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<!DOCTYPE MyDocument [<!ENTITY number \"42\">]><root></root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_entity_declaration runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnErrorTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_error(const Glib::ustring& text) override
+  {
+    throw std::runtime_error("on_error runtime exception");
+  }
+};
+
+void test_on_error()
+{
+  OnErrorTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root>&unknown;</root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_error runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnGetEntityTestParser : public xmlpp::SaxParser
+{
+public:
+  OnGetEntityTestParser()
+    : xmlpp::SaxParser(true)
+  {
+  }
+protected:
+  _xmlEntity* on_get_entity(const Glib::ustring& name) override
+  {
+    g_assert_cmpstr(name.c_str(), ==, "number");
+    throw std::runtime_error("on_get_entity runtime exception");
+  }
+};
+
+void test_on_get_entity()
+{
+  OnGetEntityTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<!DOCTYPE MyDocument [<!ENTITY number \"42\">]><root>&number;</root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_get_entity runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnStartDocumentTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_start_document() override
+  {
+    throw std::runtime_error("on_start_document runtime exception");
+  }
+};
+
+void test_on_start_document()
+{
+  OnStartDocumentTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<root></root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_start_document runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+class OnStartElementTestParser : public xmlpp::SaxParser
+{
+protected:
+  void on_start_element(const Glib::ustring& name, const xmlpp::SaxParser::AttributeList& attributes) 
override
+  {
+    g_assert_cmpstr(name.c_str(), ==, "b:root");
+    g_assert_cmpint(attributes.size(), ==, 2);
+    throw std::runtime_error("on_start_element runtime exception");
+  }
+};
+
+void test_on_start_element()
+{
+  OnStartElementTestParser parser;
+  bool exceptionThrown = false;
+  try
+  {
+    parser.parse_memory("<b:root xmlns:b=\"urn:test\" someattr=\"test\"></b:root>");
+  }
+  catch(const std::runtime_error& e)
+  {
+    exceptionThrown = true;
+    g_assert_cmpstr(e.what(), ==, "on_start_element runtime exception");
+  }
+  g_assert_true(exceptionThrown);
+}
+
+
+int main()
+{
+  Glib::init();
+
+  test_on_cdata_block();
+  test_on_characters();
+  test_on_comment();
+  test_on_end_document();
+  test_on_end_element();
+  test_on_entity_declaration();
+  test_on_error();
+  // TODO test on_fatal_error()
+  // This is not currently possible because the fatalError handler is never called;
+  // error is called for all errors.
+  // http://xmlsoft.org/html/libxml-parser.html#fatalErrorSAXFunc
+  test_on_get_entity();
+  // TODO test on_internal_subset()
+  test_on_start_document();
+  test_on_start_element();
+  // TODO test on_warning(), on_validity_error(), on_validity_warning()
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/saxparser_parse_stream_inconsistent_state/main.cc 
b/tests/saxparser_parse_stream_inconsistent_state/main.cc
new file mode 100644
index 0000000..ea8cf44
--- /dev/null
+++ b/tests/saxparser_parse_stream_inconsistent_state/main.cc
@@ -0,0 +1,76 @@
+/* Copyright (C) 2015  The libxml++ development team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <cstdlib>
+#include <glibmm.h>
+#include <sstream>
+#include <stdexcept>
+
+#include <libxml++/libxml++.h>
+
+class MySaxParser : public xmlpp::SaxParser
+{
+protected:
+  void on_start_document() override
+  {
+    throw std::runtime_error("some custom runtime exception");
+  }
+};
+
+int main()
+{
+  Glib::init();
+
+  {
+    MySaxParser parser;
+
+    bool exceptionThrown = false;
+    try
+    {
+      std::stringstream ss("<root></root>");
+      parser.parse_stream(ss);
+    }
+    catch(const std::runtime_error& e)
+    {
+      exceptionThrown = true;
+      g_assert_cmpstr(e.what(), ==, "some custom runtime exception");
+    }
+    g_assert_true(exceptionThrown);
+
+    // Try parsing a different stream.
+    exceptionThrown = false;
+    try
+    {
+      std::stringstream ss("<root></root>");
+      parser.parse_stream(ss);
+    }
+    catch(const xmlpp::parse_error& e)
+    {
+      // An "Attempt to start a second parse while a parse is in progress." parse
+      // error should not have been thrown.
+      g_assert_not_reached();
+    }
+    catch(const std::runtime_error& e)
+    {
+      exceptionThrown = true;
+      g_assert_cmpstr(e.what(), ==, "some custom runtime exception");
+    }
+    g_assert_true(exceptionThrown);
+  }
+
+  return EXIT_SUCCESS;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]