1 | // |
2 | // Copyright (c) 2017 James E. King III |
3 | // |
4 | // Distributed under the Boost Software License, Version 1.0. |
5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | // https://www.boost.org/LICENSE_1_0.txt) |
7 | // |
8 | // Mocks are used to test sad paths by forcing error responses |
9 | // |
10 | |
11 | #include <boost/core/ignore_unused.hpp> |
12 | #include <boost/uuid/detail/random_provider_detect_platform.hpp> |
13 | |
14 | #if defined(BOOST_UUID_TEST_RANDOM_MOCK) |
15 | |
16 | #if defined(BOOST_UUID_RANDOM_PROVIDER_WINCRYPT) || defined(BOOST_UUID_RANDOM_PROVIDER_POSIX) |
17 | #define BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE BOOST_SYMBOL_IMPORT |
18 | #else |
19 | #define BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE |
20 | #endif |
21 | |
22 | //! \returns true if the provider can be mocked - if not then the test |
23 | //! should skip negative testing |
24 | BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE bool expectations_capable(); |
25 | |
26 | //! Ensure all expectations for calls were consumed. This means the number |
27 | //! of expected calls was met. |
28 | //! \returns true if all expectations were met |
29 | BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE bool expectations_met(); |
30 | |
31 | //! Set the response of the next mocked random/crypto call - builds up |
32 | //! a queue of responses. If the queue empties and another call is made, |
33 | //! the test will core. |
34 | //! \param[in] success true for success response, false for failure |
35 | BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE void expect_next_call_success(bool success); |
36 | |
37 | //! \returns true if the provider acquires a context |
38 | BOOST_UUID_TEST_RANDOM_MOCK_LINKAGE bool provider_acquires_context(); |
39 | |
40 | #if defined(BOOST_UUID_RANDOM_PROVIDER_ARC4RANDOM) |
41 | |
42 | // arc4random cannot fail therefore it needs no mocking at all! |
43 | |
44 | bool expectations_capable() |
45 | { |
46 | return false; |
47 | } |
48 | |
49 | bool expectations_met() |
50 | { |
51 | throw std::logic_error("expectations not supported" ); |
52 | } |
53 | |
54 | void expect_next_call_success(bool success) |
55 | { |
56 | boost::ignore_unused(success); |
57 | throw std::logic_error("expectations not supported" ); |
58 | } |
59 | |
60 | bool provider_acquires_context() |
61 | { |
62 | throw std::logic_error("expectations not supported" ); |
63 | } |
64 | |
65 | #elif defined(BOOST_UUID_RANDOM_PROVIDER_BCRYPT) |
66 | |
67 | #include <boost/winapi/bcrypt.hpp> |
68 | #include <deque> |
69 | std::deque<boost::winapi::NTSTATUS_> bcrypt_next_result; |
70 | |
71 | bool expectations_capable() |
72 | { |
73 | return true; |
74 | } |
75 | |
76 | bool expectations_met() |
77 | { |
78 | return bcrypt_next_result.empty(); |
79 | } |
80 | |
81 | void expect_next_call_success(bool success) |
82 | { |
83 | bcrypt_next_result.push_back(success ? 0 : 17); |
84 | } |
85 | |
86 | bool provider_acquires_context() |
87 | { |
88 | return true; |
89 | } |
90 | |
91 | boost::winapi::NTSTATUS_ BOOST_WINAPI_WINAPI_CC |
92 | BCryptOpenAlgorithmProvider( |
93 | boost::winapi::BCRYPT_ALG_HANDLE_ *phAlgorithm, |
94 | boost::winapi::LPCWSTR_ pszAlgId, |
95 | boost::winapi::LPCWSTR_ pszImplementation, |
96 | boost::winapi::DWORD_ dwFlags |
97 | ) |
98 | { |
99 | boost::ignore_unused(phAlgorithm); |
100 | boost::ignore_unused(pszAlgId); |
101 | boost::ignore_unused(pszImplementation); |
102 | boost::ignore_unused(dwFlags); |
103 | |
104 | boost::winapi::NTSTATUS_ result = bcrypt_next_result.front(); |
105 | bcrypt_next_result.pop_front(); |
106 | return result; |
107 | } |
108 | |
109 | boost::winapi::NTSTATUS_ BOOST_WINAPI_WINAPI_CC |
110 | BCryptGenRandom( |
111 | boost::winapi::BCRYPT_ALG_HANDLE_ hAlgorithm, |
112 | boost::winapi::PUCHAR_ pbBuffer, |
113 | boost::winapi::ULONG_ cbBuffer, |
114 | boost::winapi::ULONG_ dwFlags |
115 | ) |
116 | { |
117 | boost::ignore_unused(hAlgorithm); |
118 | boost::ignore_unused(pbBuffer); |
119 | boost::ignore_unused(cbBuffer); |
120 | boost::ignore_unused(dwFlags); |
121 | |
122 | boost::winapi::NTSTATUS_ result = bcrypt_next_result.front(); |
123 | bcrypt_next_result.pop_front(); |
124 | return result; |
125 | } |
126 | |
127 | // the implementation ignores the result of close because it |
128 | // happens in a destructor |
129 | boost::winapi::NTSTATUS_ BOOST_WINAPI_WINAPI_CC |
130 | BCryptCloseAlgorithmProvider( |
131 | boost::winapi::BCRYPT_ALG_HANDLE_ hAlgorithm, |
132 | boost::winapi::ULONG_ dwFlags |
133 | ) |
134 | { |
135 | boost::ignore_unused(hAlgorithm); |
136 | boost::ignore_unused(dwFlags); |
137 | return 0; |
138 | } |
139 | |
140 | #elif defined(BOOST_UUID_RANDOM_PROVIDER_GETRANDOM) |
141 | |
142 | #include <deque> |
143 | #include <unistd.h> |
144 | std::deque<bool> getrandom_next_result; |
145 | |
146 | bool expectations_capable() |
147 | { |
148 | return true; |
149 | } |
150 | |
151 | bool expectations_met() |
152 | { |
153 | return getrandom_next_result.empty(); |
154 | } |
155 | |
156 | void expect_next_call_success(bool success) |
157 | { |
158 | getrandom_next_result.push_back(success); |
159 | } |
160 | |
161 | bool provider_acquires_context() |
162 | { |
163 | return false; |
164 | } |
165 | |
166 | ssize_t mock_getrandom(void *buffer, size_t length, unsigned int flags) |
167 | { |
168 | boost::ignore_unused(buffer); |
169 | boost::ignore_unused(length); |
170 | boost::ignore_unused(flags); |
171 | |
172 | bool success = getrandom_next_result.front(); |
173 | getrandom_next_result.pop_front(); |
174 | return success ? static_cast< ssize_t >(length) : static_cast< ssize_t >(-1); |
175 | } |
176 | |
177 | #define BOOST_UUID_RANDOM_PROVIDER_GETRANDOM_IMPL_GETRANDOM ::mock_getrandom |
178 | |
179 | #elif defined(BOOST_UUID_RANDOM_PROVIDER_GETENTROPY) |
180 | |
181 | // |
182 | // This stubbing technique works on unix because of how the loader resolves |
183 | // functions. Locally defined functions resolve first. |
184 | // |
185 | |
186 | #include <deque> |
187 | #include <unistd.h> |
188 | std::deque<int> getentropy_next_result; |
189 | |
190 | bool expectations_capable() |
191 | { |
192 | return true; |
193 | } |
194 | |
195 | bool expectations_met() |
196 | { |
197 | return getentropy_next_result.empty(); |
198 | } |
199 | |
200 | void expect_next_call_success(bool success) |
201 | { |
202 | getentropy_next_result.push_back(success ? 0 : -1); |
203 | } |
204 | |
205 | bool provider_acquires_context() |
206 | { |
207 | return false; |
208 | } |
209 | |
210 | int getentropy(void *buffer, size_t length) |
211 | { |
212 | boost::ignore_unused(buffer); |
213 | boost::ignore_unused(length); |
214 | |
215 | int result = getentropy_next_result.front(); |
216 | getentropy_next_result.pop_front(); |
217 | return result; |
218 | } |
219 | |
220 | #elif defined(BOOST_UUID_RANDOM_PROVIDER_POSIX) |
221 | |
222 | #include <boost/numeric/conversion/cast.hpp> |
223 | #include <deque> |
224 | #include <stdexcept> |
225 | std::deque<bool> posix_next_result; // bool success |
226 | |
227 | bool expectations_capable() |
228 | { |
229 | return true; |
230 | } |
231 | |
232 | bool expectations_met() |
233 | { |
234 | return posix_next_result.empty(); |
235 | } |
236 | |
237 | void expect_next_call_success(bool success) |
238 | { |
239 | posix_next_result.push_back(success); |
240 | } |
241 | |
242 | bool provider_acquires_context() |
243 | { |
244 | return true; |
245 | } |
246 | |
247 | int mockopen(const char *fname, int flags) |
248 | { |
249 | boost::ignore_unused(fname); |
250 | boost::ignore_unused(flags); |
251 | |
252 | bool success = posix_next_result.front(); |
253 | posix_next_result.pop_front(); |
254 | return success ? 17 : -1; |
255 | } |
256 | |
257 | ssize_t mockread(int fd, void *buf, size_t siz) |
258 | { |
259 | boost::ignore_unused(fd); |
260 | boost::ignore_unused(buf); |
261 | |
262 | // first call siz is 4, in a success case we return 1 |
263 | // forcing a second loop to come through size 3 |
264 | |
265 | if (siz < 4) { return boost::numeric_cast<ssize_t>(siz); } |
266 | if (siz > 4) { throw std::logic_error("unexpected siz" ); } |
267 | |
268 | bool success = posix_next_result.front(); |
269 | posix_next_result.pop_front(); |
270 | return success ? 1 : -1; |
271 | } |
272 | |
273 | #define BOOST_UUID_RANDOM_PROVIDER_POSIX_IMPL_OPEN mockopen |
274 | #define BOOST_UUID_RANDOM_PROVIDER_POSIX_IMPL_READ mockread |
275 | |
276 | #elif defined(BOOST_UUID_RANDOM_PROVIDER_WINCRYPT) |
277 | |
278 | // Nothing to declare, since the expectation methods were already |
279 | // defined as import, we will link against a mock library |
280 | |
281 | #else |
282 | |
283 | #error support needed here for testing |
284 | |
285 | #endif |
286 | |
287 | #endif // BOOST_UUID_TEST_RANDOM_MOCK |
288 | |