summaryrefslogtreecommitdiff
path: root/indra/test/catch_and_store_what_in.h
blob: 5beba06024368dd878bd207484055c7600c3e7d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
 * @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<std::runtime_error>(
 *     [](){ throw std::runtime_error("badness"); });
 * ensure_equals(threw, "badness");
 * @endcode
 */
template <typename EXCEPTION, typename FUNC>
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) */