summaryrefslogtreecommitdiff
path: root/indra/test/catch_and_store_what_in.h
blob: d3dada2acf88ca1f1acd2d4ad5c44bc2de63e936 (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) */