36
36
rcutils_fault_injection_is_test_complete (void );
37
37
38
38
/**
39
- * \def RCUTILS_FAULT_INJECTION_SET_COUNT
40
39
* \brief Atomically set the fault injection counter.
41
40
*
42
- * There will be at most one fault injected failure per call to RCUTILS_FAULT_INJECTION_SET_COUNT.
43
- * To test all reachable fault injection locations, call this macro inside a loop and set the count
44
- * to an incrementing count variable.
41
+ * This is typically not the preferred method of interacting directly with the fault injection
42
+ * logic, instead use `RCUTILS_FAULT_INJECTION_TEST` instead.
45
43
*
46
- * for (int i = 0; i < SUFFICIENTLY_LARGE_ITERATION_COUNT; ++i) {
47
- * RCUTILS_FAULT_INJECTION_SET_COUNT(i);
48
- * ... // Call function under test
49
- * }
50
- * ASSERT_LT(RCUTILS_FAULT_INJECTION_NEVER_FAIL, RCUTILS_FAULT_INJECTION_GET_COUNT());
44
+ * This function may also be used for pausing code inside of a `RCUTILS_FAULT_INJECTION_TEST` with
45
+ * something like the following:
51
46
*
52
- * Where SUFFICIENTLY_LARGE_ITERATION_COUNT is a value larger than the maximum expected calls to
53
- * `RCUTILS_FAULT_INJECTION_MAYBE_RETURN_ERROR`. This last assertion just ensures that your choice for
54
- * SUFFICIENTLY_LARGE_ITERATION_COUNT was large enough. To avoid having to choose this count
55
- * yourself, you can use a do-while loop.
56
- *
57
- * int i = 0;
58
- * do {
59
- * RCUTILS_FAULT_INJECTION_SET_COUNT(i++);
60
- * ... // Call function under test
61
- * } while (RCUTILS_FAULT_INJECTION_GET_COUNT() <= RCUTILS_FAULT_INJECTION_NEVER_FAIL);
47
+ * RCUTILS_FAULT_INJECTION_TEST({
48
+ * ... // code to run with fault injection
49
+ * int64_t count = rcutils_fault_injection_get_count();
50
+ * rcutils_fault_injection_set_count(RCUTILS_FAULT_INJECTION_NEVER_FAIL);
51
+ * ... // code to run without fault injection
52
+ * rcutils_fault_injection_set_count(count);
53
+ * ... // code to run with fault injection
54
+ * });
62
55
*
63
56
* \param count The count to set the fault injection counter to. If count is negative, then fault
64
57
* injection errors will be disabled. The counter is globally initialized to
69
62
rcutils_fault_injection_set_count (int count );
70
63
71
64
/**
72
- * \def RCUTILS_FAULT_INJECTION_GET_COUNT
73
65
* \brief Atomically get the fault injection counter value
74
66
*
75
- * Use this macro after running the code under test to check whether the counter reached a negative
76
- * value. This is helpful so you can verify that you ran the fault injection test in a loop a
77
- * sufficient number of times. Likewise, if the code under test returned with an error, but the
78
- * count value was greater or equal to 0, then the failure was not caused by the fault injection
79
- * counter.
67
+ * This function is typically not used directly but instead indirectly inside an
68
+ * `RCUTILS_FAULT_INJECTION_TEST`
80
69
*/
81
70
RCUTILS_PUBLIC
82
71
RCUTILS_WARN_UNUSED
83
72
int_least64_t
84
73
rcutils_fault_injection_get_count (void );
85
74
75
+ /**
76
+ * \brief Implementation of fault injection decrementer
77
+ *
78
+ * This is included inside of macros, so it needs to be exported as a public function, but it
79
+ * should not be used directly.
80
+ */
86
81
RCUTILS_PUBLIC
87
82
RCUTILS_WARN_UNUSED
88
83
int_least64_t
@@ -114,8 +109,8 @@ _rcutils_fault_injection_maybe_fail(void);
114
109
/**
115
110
* \def RCUTILS_FAULT_INJECTION_MAYBE_FAIL
116
111
* \brief This macro checks and decrements a static global variable atomic counter and executes
117
- * `failure_code` if the counter is 0 inside a scoped block (any variables declared in failure_code)
118
- * will not be avaliable outside of this scoped block.
112
+ * `failure_code` if the counter is 0 inside a scoped block (any variables declared in
113
+ * failure_code) will not be avaliable outside of this scoped block.
119
114
*
120
115
* This macro is not a function itself, so it will cause the calling function to execute the code
121
116
* from within an if loop.
@@ -135,6 +130,33 @@ _rcutils_fault_injection_maybe_fail(void);
135
130
failure_code; \
136
131
}
137
132
133
+ /**
134
+ * \def RCUTILS_FAULT_INJECTION_TEST
135
+ *
136
+ * The fault injection macro for use with unit tests to check that `code` can tolerate injected
137
+ * failures at all points along the execution path where the indicating macros
138
+ * `RCUTILS_CAN_RETURN_WITH_ERROR_OF` and `RCUTILS_CAN_FAIL_WITH` are located.
139
+ *
140
+ * This macro is intended to be used within a gtest function macro like 'TEST', 'TEST_F', etc.
141
+ *
142
+ * `code` is executed within a do-while loop and therefore any variables declared within are in
143
+ * their own scope block.
144
+ *
145
+ * Here's a simple example:
146
+ * RCUTILS_FAULT_INJECTION_TEST(
147
+ * rcl_ret_t ret = rcl_init(argc, argv, options, context);
148
+ * if (RCL_RET_OK == ret)
149
+ * {
150
+ * ret = rcl_shutdown(context);
151
+ * }
152
+ * });
153
+ *
154
+ * In this example, you will need have conditional execution based on the return value of
155
+ * `rcl_init`. If it failed, then it wouldn't make sense to call rcl_shutdown. In your own test,
156
+ * there might be similar logic that requires conditional checks. The goal of writing this test
157
+ * is less about checking the behavior is consistent, but instead that failures do not cause
158
+ * program crashes, memory errors, or unnecessary memory leaks.
159
+ */
138
160
#define RCUTILS_FAULT_INJECTION_TEST (code ) \
139
161
do { \
140
162
int fault_injection_count = 0; \
0 commit comments