| 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"; } |
|
sergei
2015/04/01 07:16:02
JIC, actually, these three methods as well as the
|
| + 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())); |
| +} |