| Index: test/plugin/ExceptionTest.cpp | 
| =================================================================== | 
| new file mode 100644 | 
| --- /dev/null | 
| +++ b/test/plugin/ExceptionTest.cpp | 
| @@ -0,0 +1,385 @@ | 
| +/* | 
| + * 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"; | 
| +  }; | 
| +}; | 
| + | 
| +struct NullHandlers | 
| +{ | 
| +  static void Unknown(int y) {} | 
| +  static void Exception(std::exception& ex,int y) {} | 
| +  static void LogicError(std::logic_error& ex,int y) { Exception(ex, y); } | 
| +  static void RuntimeError(std::runtime_error& ex,int y) { Exception(ex, y); } | 
| +  static void SystemError(std::system_error& ex,int y) { RuntimeError(ex, y); } | 
| +}; | 
| + | 
| +/* | 
| + * 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 ExceptionCode | 
| +{ | 
| +  InvalidCode = -1, | 
| +  UnknownCode = 1, | 
| +  ExceptionCode, | 
| +  LogicErrorCode, | 
| +  RuntimeErrorCode, | 
| +  SystemErrorCode | 
| +}; | 
| + | 
| +struct NullHandlersReturn | 
| +{ | 
| +  typedef int return_t; | 
| +  static return_t Unknown(int) { return UnknownCode; } | 
| +  static return_t Exception(std::exception& ex, int) { return ExceptionCode; } | 
| +  static return_t LogicError(std::logic_error& ex, int) { return LogicErrorCode; } | 
| +  static return_t RuntimeError(std::runtime_error& ex, int) { return RuntimeErrorCode; } | 
| +  static return_t SystemError(std::system_error& ex, int) { return SystemErrorCode; } | 
| +}; | 
| + | 
| +template<class X> | 
| +void TrivialReturn(int n, X x) | 
| +{ | 
| +  ASSERT_NO_THROW( | 
| +  { | 
| +    try | 
| +    { | 
| +      throw x; | 
| +    } | 
| +    catch (...) | 
| +    { | 
| +      ASSERT_EQ(n, CatchAllReturn<NullHandlersReturn>::Handler(0)); | 
| +    } | 
| +  }); | 
| +} | 
| + | 
| +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(0); | 
| +    } | 
| +  }); | 
| +  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(int) { FAIL() << "Unexpected exception of unknown type"; } | 
| +  static void Exception(std::exception& ex, int) { FAIL() << "Unexpected std::exception"; } | 
| +  static void LogicError(std::logic_error& ex, int) { FAIL() << "Unexpected std::logic_error"; } | 
| +  static void RuntimeError(std::runtime_error& ex, int) { FAIL() << "Unexpected std::runtime_error"; } | 
| +  static void SystemError(std::system_error& ex, int) { FAIL() << "Unexpected std::system_error"; }; | 
| +protected: | 
| +  static void Ping() { SimpleResult = SimpleExpected; } | 
| +}; | 
| + | 
| +TEST(Exception, SimpleVoidUnknown) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersBase | 
| +  { | 
| +    static void Unknown(int) { Ping(); } | 
| +  }; | 
| +  SimpleVoid(5, Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleVoidException) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersBase | 
| +  { | 
| +    static void Exception(std::exception& ex, int) { Ping(); } | 
| +  }; | 
| +  SimpleVoid(PlainException(), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleVoidLogicError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersBase | 
| +  { | 
| +    static void LogicError(std::logic_error& ex, int) { Ping(); } | 
| +  }; | 
| +  SimpleVoid(std::logic_error(""), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleVoidRuntimeError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersBase | 
| +  { | 
| +    static void RuntimeError(std::runtime_error& ex, int) { Ping(); } | 
| +  }; | 
| +  SimpleVoid(std::runtime_error(""), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleVoidSystemError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersBase | 
| +  { | 
| +    static void SystemError(std::system_error& ex, int) { 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(0)); | 
| +    } | 
| +  }); | 
| +  EXPECT_EQ(SimpleExpected, SimpleResult); | 
| +} | 
| + | 
| +/* | 
| + * The base handlers class fails every execution path. | 
| + */ | 
| +class SimpleHandlersReturnBase | 
| +{ | 
| +public: | 
| +  typedef int return_t; | 
| +  static return_t Unknown(int) { ADD_FAILURE() << "Unexpected exception of unknown type"; return InvalidCode; } | 
| +  static return_t Exception(std::exception& ex, int) { ADD_FAILURE() << "Unexpected std::exception"; return InvalidCode; } | 
| +  static return_t LogicError(std::logic_error& ex, int) { ADD_FAILURE() << "Unexpected std::logic_error"; return InvalidCode; } | 
| +  static return_t RuntimeError(std::runtime_error& ex, int) { ADD_FAILURE() << "Unexpected std::runtime_error"; return InvalidCode; } | 
| +  static return_t SystemError(std::system_error& ex, int) { ADD_FAILURE() << "Unexpected std::system_error"; return InvalidCode; } | 
| +protected: | 
| +  static void Ping() { SimpleResult = SimpleExpected; } | 
| +}; | 
| + | 
| +TEST(Exception, SimpleReturnUnknown) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersReturnBase | 
| +  { | 
| +    static int Unknown(int) { Ping(); return UnknownCode; } | 
| +  }; | 
| +  SimpleReturn(UnknownCode, 5, Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleReturnException) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersReturnBase | 
| +  { | 
| +    static int Exception(std::exception& ex, int) { Ping(); return ExceptionCode; } | 
| +  }; | 
| +  SimpleReturn(ExceptionCode, PlainException(), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleReturnLogicError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersReturnBase | 
| +  { | 
| +    static int LogicError(std::logic_error& ex, int) { Ping(); return LogicErrorCode; } | 
| +  }; | 
| +  SimpleReturn(LogicErrorCode, std::logic_error(""), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleReturnRuntimeError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersReturnBase | 
| +  { | 
| +    static int RuntimeError(std::runtime_error& ex, int) { Ping(); return RuntimeErrorCode; } | 
| +  }; | 
| +  SimpleReturn(RuntimeErrorCode, std::runtime_error(""), Handler()); | 
| +} | 
| + | 
| +TEST(Exception, SimpleReturnSystemError) | 
| +{ | 
| +  struct Handler : | 
| +    public SimpleHandlersReturnBase | 
| +  { | 
| +    static int SystemError(std::system_error& ex, int) { Ping(); return SystemErrorCode; } | 
| +  }; | 
| +  SimpleReturn(SystemErrorCode, std::system_error(std::error_code()), Handler()); | 
| +} | 
| + | 
|  |