Index: test/plugin/ExceptionTest.cpp |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/test/plugin/ExceptionTest.cpp |
@@ -0,0 +1,376 @@ |
+/* |
+ * 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 (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"; |
+ }; |
+}; |
+ |
+/* |
+ * 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(); |
+ } |
+ }); |
+} |
+ |
+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 ExceptionCode |
+{ |
+ InvalidCode = -1, |
+ UnknownCode = 1, |
+ ExceptionCode, |
+ LogicErrorCode, |
+ RuntimeErrorCode, |
+ SystemErrorCode |
+}; |
+ |
+struct NullHandlersReturn |
+{ |
+ typedef int return_t; |
+ static return_t Unknown() { return UnknownCode; } |
+ static return_t Exception(std::exception& ex) { return ExceptionCode; } |
+ static return_t LogicError(std::logic_error& ex) { return LogicErrorCode; } |
+ static return_t RuntimeError(std::runtime_error& ex) { return RuntimeErrorCode; } |
+ static return_t SystemError(std::system_error& ex) { return SystemErrorCode; } |
+}; |
+ |
+template<class X> |
+void TrivialReturn(int n, X x) |
+{ |
+ ASSERT_NO_THROW( |
+ { |
+ try |
+ { |
+ throw x; |
+ } |
+ catch (...) |
+ { |
+ ASSERT_EQ(n, CatchAllReturn<NullHandlersReturn>::Handler()); |
+ } |
+ }); |
+} |
+ |
+TEST(Exception, TrivialReturnUnknown) |
+{ |
+ TrivialReturn(UnknownCode, 5); |
+} |
+ |
+TEST(Exception, TrivialReturnException) |
+{ |
+ TrivialReturn(ExceptionCode, PlainException()); |
+} |
+ |
+TEST(Exception, TrivialReturnLogicError) |
+{ |
+ TrivialReturn(LogicErrorCode, std::logic_error("")); |
+} |
+ |
+TEST(Exception, TrivialReturnRuntimeError) |
+{ |
+ TrivialReturn(RuntimeErrorCode, std::runtime_error("")); |
+} |
+ |
+TEST(Exception, TrivialReturnSystemError) |
+{ |
+ TrivialReturn(SystemErrorCode, std::system_error(std::error_code())); |
+} |
+ |
+/* |
+ * The simple tests ensure that the flow of control arrives in the correct subhandler. |
+ */ |
+template<class X, class H> |
+void SimpleVoid(X x, H h) |
+{ |
+ SimpleResult = 0; |
+ SimpleExpected = rand(); |
+ if (SimpleExpected == 0) { SimpleExpected = 1; } |
+ ASSERT_NO_THROW( |
+ { |
+ try |
+ { |
+ throw x; |
+ } |
+ catch (...) |
+ { |
+ CatchAllVoid<H>::Handler(); |
+ } |
+ }); |
+ EXPECT_EQ(SimpleExpected, SimpleResult); |
+} |
+ |
+/* |
+ * VS 2012 supports thread_local semantics for POD only, not arbitrary types. |
+ * That's good enough for now. |
+ * Remove the definition when VS matures. |
+ */ |
+#define thread_local __declspec(thread) |
+ |
+/* |
+ * The sub-handlers are purely static functions, |
+ * so getting them to return something unique for testing has possible race conditions. |
+ * We're using a thread-local variable as a return code from the simple handlers, |
+ * which ensures that even a multi-threaded test runner will work here. |
+ */ |
+thread_local int SimpleResult; |
+thread_local int SimpleExpected; |
+ |
+/* |
+ * 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: |
+ static void Unknown() { FAIL() << "Unexpected exception of unknown type"; } |
+ static void Exception(std::exception& ex) { FAIL() << "Unexpected std::exception"; } |
+ static void LogicError(std::logic_error& ex) { FAIL() << "Unexpected std::logic_error"; } |
+ static void RuntimeError(std::runtime_error& ex) { FAIL() << "Unexpected std::runtime_error"; } |
+ static void SystemError(std::system_error& ex) { FAIL() << "Unexpected std::system_error"; }; |
+protected: |
+ static void Ping() { SimpleResult = SimpleExpected; } |
+}; |
+ |
+TEST(Exception, SimpleVoidUnknown) |
+{ |
+ struct Handler : |
+ public SimpleHandlersBase |
+ { |
+ static void Unknown() { Ping(); } |
+ }; |
+ SimpleVoid(5, Handler()); |
+} |
+ |
+TEST(Exception, SimpleVoidException) |
+{ |
+ struct Handler : |
+ public SimpleHandlersBase |
+ { |
+ static void Exception(std::exception& ex) { Ping(); } |
+ }; |
+ SimpleVoid(PlainException(), Handler()); |
+} |
+ |
+TEST(Exception, SimpleVoidLogicError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersBase |
+ { |
+ static void LogicError(std::logic_error& ex) { Ping(); } |
+ }; |
+ SimpleVoid(std::logic_error(""), Handler()); |
+} |
+ |
+TEST(Exception, SimpleVoidRuntimeError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersBase |
+ { |
+ static void RuntimeError(std::runtime_error& ex) { Ping(); } |
+ }; |
+ SimpleVoid(std::runtime_error(""), Handler()); |
+} |
+ |
+TEST(Exception, SimpleVoidSystemError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersBase |
+ { |
+ static void SystemError(std::system_error& ex) { Ping(); } |
+ }; |
+ SimpleVoid(std::system_error(std::error_code()), Handler()); |
+} |
+ |
+template<class X, class H> |
+void SimpleReturn(int n, X x, H h) |
+{ |
+ SimpleResult = 0; |
+ SimpleExpected = rand(); |
+ if (SimpleExpected == 0) { SimpleExpected = 1; } |
+ ASSERT_NO_THROW( |
+ { |
+ try |
+ { |
+ throw x; |
+ } |
+ catch (...) |
+ { |
+ EXPECT_EQ(n, CatchAllReturn<H>::Handler()); |
+ } |
+ }); |
+ EXPECT_EQ(SimpleExpected, SimpleResult); |
+} |
+ |
+/* |
+ * The base handlers class fails every execution path. |
+ */ |
+class SimpleHandlersReturnBase |
+{ |
+public: |
+ typedef int return_t; |
+ static return_t Unknown() { ADD_FAILURE() << "Unexpected exception of unknown type"; return InvalidCode; } |
+ static return_t Exception(std::exception& ex) { ADD_FAILURE() << "Unexpected std::exception"; return InvalidCode; } |
+ static return_t LogicError(std::logic_error& ex) { ADD_FAILURE() << "Unexpected std::logic_error"; return InvalidCode; } |
+ static return_t RuntimeError(std::runtime_error& ex) { ADD_FAILURE() << "Unexpected std::runtime_error"; return InvalidCode; } |
+ static return_t SystemError(std::system_error& ex) { ADD_FAILURE() << "Unexpected std::system_error"; return InvalidCode; } |
+protected: |
+ static void Ping() { SimpleResult = SimpleExpected; } |
+}; |
+ |
+TEST(Exception, SimpleReturnUnknown) |
+{ |
+ struct Handler : |
+ public SimpleHandlersReturnBase |
+ { |
+ static int Unknown() { Ping(); return UnknownCode; } |
+ }; |
+ SimpleReturn(UnknownCode, 5, Handler()); |
+} |
+ |
+TEST(Exception, SimpleReturnException) |
+{ |
+ struct Handler : |
+ public SimpleHandlersReturnBase |
+ { |
+ static int Exception(std::exception& ex) { Ping(); return ExceptionCode; } |
+ }; |
+ SimpleReturn(ExceptionCode, PlainException(), Handler()); |
+} |
+ |
+TEST(Exception, SimpleReturnLogicError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersReturnBase |
+ { |
+ static int LogicError(std::logic_error& ex) { Ping(); return LogicErrorCode; } |
+ }; |
+ SimpleReturn(LogicErrorCode, std::logic_error(""), Handler()); |
+} |
+ |
+TEST(Exception, SimpleReturnRuntimeError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersReturnBase |
+ { |
+ static int RuntimeError(std::runtime_error& ex) { Ping(); return RuntimeErrorCode; } |
+ }; |
+ SimpleReturn(RuntimeErrorCode, std::runtime_error(""), Handler()); |
+} |
+ |
+TEST(Exception, SimpleReturnSystemError) |
+{ |
+ struct Handler : |
+ public SimpleHandlersReturnBase |
+ { |
+ static int SystemError(std::system_error& ex) { Ping(); return SystemErrorCode; } |
+ }; |
+ SimpleReturn(SystemErrorCode, std::system_error(std::error_code()), Handler()); |
+} |
+ |