Index: test/plugin/ExceptionTest.cpp
diff --git a/test/plugin/ExceptionTest.cpp b/test/plugin/ExceptionTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e67e4103ac18f280edb548d1a18f742336031f0a
--- /dev/null
+++ b/test/plugin/ExceptionTest.cpp
@@ -0,0 +1,387 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2015 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include "../../src/plugin/Exception.h"
+#include <stdexcept>
+
+void AssignCurrentException(std::exception_ptr& e)
+{
+  e = std::current_exception();
+}
+
+/*
+ * This test verifies that std::current_exception works during exception handling generally,
+ *   not just in the immediate context of a 'catch' clause.
+ */
+TEST(Exception, CurrentExceptionWorksOutsideCatchHandler)
+{
+  std::exception_ptr ep;
+  const auto e = std::runtime_error("BJTCiRhkVAmvMg");
+  try
+  {
+    throw e;
+  }
+  catch (...)
+  {
+    AssignCurrentException(ep);
+    ASSERT_TRUE(ep);
+    /*
+     * You might think that the next set would be EXPECT_EQ betwwen the thrown exception and the original one.
+     * Such a test does not pass, nor is in necessary.
+     * The throw statement above is throwing by value, so it makes a copy.
+     */
+  }
+  try
+  {
+    rethrow_exception(ep);
+    FAIL() << "Statement after 'rethrow_exception' executed.";
+  }
+  catch (const std::runtime_error& ex)
+  {
+    EXPECT_STREQ(e.what(), ex.what());
+    return;
+  }
+  catch (...)
+  {
+    FAIL() << "Rethrown exception isn't the same type as the original";
+  }
+  FAIL() << "Did not return after catching rethrown exception";
+}
+
+/*
+ * A plain exception, not a subclass of std::runtime_error or std::logic_error.
+ */
+class PlainException
+  : public std::exception
+{
+  const char* what() const override
+  {
+    return "Plain";
+  };
+};
+
+struct NullHandlers
+{
+  static void Unknown(int) {}
+  static void Exception(const std::exception&,int) {}
+};
+
+/*
+ * The trivial tests assure that nothing escapes the catch-all function,
+ *   that is, we ensure that nothing is thrown or rethrown.
+ */
+template<class X>
+void TrivialVoid(X x)
+{
+  ASSERT_NO_THROW(
+  {
+    try
+    {
+      throw x;
+    }
+    catch (...)
+    {
+      CatchAllVoid<NullHandlers>::Handler(0);
+    }
+  });
+}
+
+TEST(Exception, TrivialVoidUnknown)
+{
+  TrivialVoid(5);
+}
+
+TEST(Exception, TrivialVoidException)
+{
+  TrivialVoid(PlainException());
+}
+
+TEST(Exception, TrivialVoidLogicError)
+{
+  TrivialVoid(std::logic_error(""));
+}
+
+TEST(Exception, TrivialVoidRuntimeError)
+{
+  TrivialVoid(std::runtime_error(""));
+}
+
+TEST(Exception, TrivialVoidSystemError)
+{
+  TrivialVoid(std::system_error(std::error_code()));
+}
+
+enum class ReturnType
+{
+  InvalidCode = -1,
+  UnknownCode = 1,
+  ExceptionCode,
+  LogicErrorCode,
+  RuntimeErrorCode,
+  SystemErrorCode
+};
+
+struct NullHandlersReturn
+{
+  typedef ReturnType ReturnType;
+  static ReturnType Unknown(int) { return ReturnType::UnknownCode; }
+  static ReturnType Exception(const std::exception&, int) { return ReturnType::ExceptionCode; }
+  static ReturnType Exception(const std::logic_error&, int) { return ReturnType::LogicErrorCode; }
+  static ReturnType Exception(const std::runtime_error&, int) { return ReturnType::RuntimeErrorCode; }
+  static ReturnType Exception(const std::system_error&, int) { return ReturnType::SystemErrorCode; }
+};
+
+template<class X>
+void TrivialReturn(ReturnType n, X x)
+{
+  ASSERT_NO_THROW(
+  {
+    try
+    {
+      throw x;
+    }
+    catch (...)
+    {
+      ASSERT_EQ(n, CatchAllReturn<NullHandlersReturn>::Handler(0));
+    }
+  });
+}
+
+TEST(Exception, TrivialReturnUnknown)
+{
+  TrivialReturn(ReturnType::UnknownCode, 5);
+}
+
+TEST(Exception, TrivialReturnException)
+{
+  TrivialReturn(ReturnType::ExceptionCode, PlainException());
+}
+
+TEST(Exception, TrivialReturnLogicError)
+{
+  TrivialReturn(ReturnType::LogicErrorCode, std::logic_error(""));
+}
+
+TEST(Exception, TrivialReturnRuntimeError)
+{
+  TrivialReturn(ReturnType::RuntimeErrorCode, std::runtime_error(""));
+}
+
+TEST(Exception, TrivialReturnSystemError)
+{
+  TrivialReturn(ReturnType::SystemErrorCode, std::system_error(std::error_code()));
+}
+
+class Touchable
+{
+public:
+  Touchable() : m_isTouched(false){}
+  bool IsTouched() const
+  {
+    return m_isTouched;
+  }
+  void Touch()
+  {
+    m_isTouched = true;
+  }
+private:
+  // In the tests we use `Touchable&` as the type for UserData, so let's make
+  // it uncopyable to be sure that it's not copied somewhere by accident.
+  Touchable(const Touchable&);
+  void operator=(const Touchable&);
+private:
+  bool m_isTouched;
+};
+
+/*
+ * The simple tests ensure that the flow of control arrives in the correct subhandler.
+ */
+template<typename H, class X>
+void SimpleVoid(X x)
+{
+  Touchable userData;
+  ASSERT_NO_THROW(
+  {
+    try
+    {
+      throw x;
+    }
+    catch (...)
+    {
+      CatchAllVoid<H>::Handler<Touchable&>(userData);
+    }
+  });
+  EXPECT_TRUE(userData.IsTouched());
+}
+
+/*
+ * The base handler class for the simple tests fails every execution path.
+ * Each specific test redefines a single one of the handler functions that it uses.
+ */
+class SimpleHandlersBase
+{
+public:
+  typedef Touchable& UserData;
+  SimpleHandlersBase(){}
+  static void Unknown(UserData) { FAIL() << "Unexpected exception of unknown type"; }
+  static void Exception(const std::exception&, UserData) { FAIL() << "Unexpected std::exception"; }
+  static void Exception(const std::logic_error&, UserData) { FAIL() << "Unexpected std::logic_error"; }
+  static void Exception(const std::runtime_error&, UserData) { FAIL() << "Unexpected std::runtime_error"; }
+  static void Exception(const std::system_error&, UserData) { FAIL() << "Unexpected std::system_error"; };
+};
+
+TEST(Exception, SimpleVoidUnknown)
+{
+  struct Handler :
+    public SimpleHandlersBase
+  {
+    static void Unknown(UserData h) { h.Touch(); }
+  };
+  SimpleVoid<Handler>(5);
+}
+
+TEST(Exception, SimpleVoidException)
+{
+  struct Handler :
+    public SimpleHandlersBase
+  {
+    static void Exception(const std::exception&, UserData h) { h.Touch(); }
+  };
+  SimpleVoid<Handler>(PlainException());
+}
+
+TEST(Exception, SimpleVoidLogicError)
+{
+  struct Handler :
+    public SimpleHandlersBase
+  {
+    static void Exception(const std::logic_error&, UserData h) { h.Touch(); }
+    using SimpleHandlersBase::Exception;
+  };
+  SimpleVoid<Handler>(std::logic_error(""));
+}
+
+TEST(Exception, SimpleVoidRuntimeError)
+{
+  struct Handler :
+    public SimpleHandlersBase
+  {
+    static void Exception(const std::runtime_error&, UserData h) { h.Touch(); }
+    using SimpleHandlersBase::Exception;
+  };
+  SimpleVoid<Handler>(std::runtime_error(""));
+}
+
+TEST(Exception, SimpleVoidSystemError)
+{
+  struct Handler :
+    public SimpleHandlersBase
+  {
+    static void Exception(const std::system_error&, UserData h) { h.Touch(); }
+    using SimpleHandlersBase::Exception;
+  };
+  SimpleVoid<Handler>(std::system_error(std::error_code()));
+}
+
+template<class H, class X>
+void SimpleReturn(ReturnType n, X x)
+{
+  Touchable userData;
+  ASSERT_NO_THROW(
+  {
+    try
+    {
+      throw x;
+    }
+    catch (...)
+    {
+      EXPECT_EQ(n, CatchAllReturn<H>::Handler<Touchable&>(userData));
+    }
+  });
+  EXPECT_TRUE(userData.IsTouched());
+}
+
+/*
+ * The base handlers class fails every execution path.
+ */
+class SimpleHandlersReturnBase
+{
+public:
+  typedef ReturnType ReturnType;
+  typedef Touchable& UserData;
+  static ReturnType Unknown(UserData) { ADD_FAILURE() << "Unexpected exception of unknown type"; return ReturnType::InvalidCode; }
+  static ReturnType Exception(const std::exception&, UserData) { ADD_FAILURE() << "Unexpected std::exception"; return ReturnType::InvalidCode; }
+  static ReturnType Exception(const std::logic_error&, UserData) { ADD_FAILURE() << "Unexpected std::logic_error"; return ReturnType::InvalidCode; }
+  static ReturnType Exception(const std::runtime_error&, UserData) { ADD_FAILURE() << "Unexpected std::runtime_error"; return ReturnType::InvalidCode; }
+  static ReturnType Exception(const std::system_error&, UserData) { ADD_FAILURE() << "Unexpected std::system_error"; return ReturnType::InvalidCode; }
+};
+
+TEST(Exception, SimpleReturnUnknown)
+{
+  struct Handler :
+    public SimpleHandlersReturnBase
+  {
+    static ReturnType Unknown(UserData h) { h.Touch(); return ReturnType::UnknownCode; }
+    using SimpleHandlersReturnBase::Exception;
+  };
+  SimpleReturn<Handler>(ReturnType::UnknownCode, 5);
+}
+
+TEST(Exception, SimpleReturnException)
+{
+  struct Handler :
+    public SimpleHandlersReturnBase
+  {
+    static ReturnType Exception(const std::exception&, UserData h) { h.Touch(); return ReturnType::ExceptionCode; }
+    using SimpleHandlersReturnBase::Exception;
+  };
+  SimpleReturn<Handler>(ReturnType::ExceptionCode, PlainException());
+}
+
+TEST(Exception, SimpleReturnLogicError)
+{
+  struct Handler :
+    public SimpleHandlersReturnBase
+  {
+    static ReturnType Exception(const std::logic_error&, UserData h) { h.Touch(); return ReturnType::LogicErrorCode; }
+    using SimpleHandlersReturnBase::Exception;
+  };
+  SimpleReturn<Handler>(ReturnType::LogicErrorCode, std::logic_error(""));
+}
+
+TEST(Exception, SimpleReturnRuntimeError)
+{
+  struct Handler :
+    public SimpleHandlersReturnBase
+  {
+    static ReturnType Exception(const std::runtime_error&, UserData h) { h.Touch(); return ReturnType::RuntimeErrorCode; }
+    using SimpleHandlersReturnBase::Exception;
+  };
+  SimpleReturn<Handler>(ReturnType::RuntimeErrorCode, std::runtime_error(""));
+}
+
+TEST(Exception, SimpleReturnSystemError)
+{
+  struct Handler :
+    public SimpleHandlersReturnBase
+  {
+    static ReturnType Exception(const std::system_error&, UserData h) { h.Touch(); return ReturnType::SystemErrorCode; }
+    using SimpleHandlersReturnBase::Exception;
+  };
+  SimpleReturn<Handler>(ReturnType::SystemErrorCode, std::system_error(std::error_code()));
+}
\ No newline at end of file
