/** * @file catch_and_store_what_in.h * @author Nat Goodspeed * @date 2012-02-15 * @brief catch_what() template function, CATCH_AND_STORE_WHAT_IN() macro * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_CATCH_AND_STORE_WHAT_IN_H) #define LL_CATCH_AND_STORE_WHAT_IN_H /** * In the brave new world of lambdas, we can use a nicer C++ idiom for testing * exceptions than CATCH_AND_STORE_WHAT_IN() below, e.g.: * * @code * std::string threw = catch_what( * [](){ throw std::runtime_error("badness"); }); * ensure_equals(threw, "badness"); * @endcode */ template std::string catch_what(FUNC func) { try { func(); return {}; } catch (const EXCEPTION& err) { return err.what(); } } /** * Idiom useful for test programs: catch an expected exception, store its * what() string in a specified std::string variable. From there the caller * can do things like: * @code * ensure("expected exception not thrown", ! string.empty()); * @endcode * or * @code * ensure_contains("exception doesn't mention blah", string, "blah"); * @endcode * etc. * * The trouble is that when linking to a dynamic libllcommon.so on Linux, we * generally fail to catch the specific exception. Oddly, we can catch it as * std::runtime_error and validate its typeid().name(), so we do -- but that's * a lot of boilerplate per test. Encapsulate with this macro. Usage: * * @code * std::string threw; * try * { * some_call_that_should_throw_Foo(); * } * CATCH_AND_STORE_WHAT_IN(threw, Foo) * ensure("some_call_that_should_throw_Foo() didn't throw", ! threw.empty()); * @endcode */ #define CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION) \ catch (const EXCEPTION& ex) \ { \ (THREW) = ex.what(); \ } \ CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION) #ifndef LL_LINUX #define CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION) \ /* only needed on Linux */ #else // LL_LINUX #define CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION) \ catch (const std::runtime_error& ex) \ { \ /* This clause is needed on Linux, on the viewer side, because */ \ /* the exception isn't caught by catch (const EXCEPTION&). */ \ /* But if the expected exception was thrown, allow the test to */ \ /* succeed anyway. Not sure how else to handle this odd case. */ \ if (std::string(typeid(ex).name()) == typeid(EXCEPTION).name()) \ { \ /* std::cerr << "Caught " << typeid(ex).name() */ \ /* << " with Linux workaround" << std::endl; */ \ (THREW) = ex.what(); \ /*std::cout << ex.what() << std::endl;*/ \ } \ else \ { \ /* We don't even recognize this exception. Let it propagate */ \ /* out to TUT to fail the test. */ \ throw; \ } \ } \ catch (...) \ { \ std::cerr << "Failed to catch expected exception " \ << #EXCEPTION << "!" << std::endl; \ /* This indicates a problem in the test that should be addressed. */ \ throw; \ } #endif // LL_LINUX #endif /* ! defined(LL_CATCH_AND_STORE_WHAT_IN_H) */