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
expectations_capable()44 bool expectations_capable()
45 {
46 return false;
47 }
48
expectations_met()49 bool expectations_met()
50 {
51 throw std::logic_error("expectations not supported");
52 }
53
expect_next_call_success(bool success)54 void expect_next_call_success(bool success)
55 {
56 boost::ignore_unused(success);
57 throw std::logic_error("expectations not supported");
58 }
59
provider_acquires_context()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
expectations_capable()71 bool expectations_capable()
72 {
73 return true;
74 }
75
expectations_met()76 bool expectations_met()
77 {
78 return bcrypt_next_result.empty();
79 }
80
expect_next_call_success(bool success)81 void expect_next_call_success(bool success)
82 {
83 bcrypt_next_result.push_back(success ? 0 : 17);
84 }
85
provider_acquires_context()86 bool provider_acquires_context()
87 {
88 return true;
89 }
90
91 boost::winapi::NTSTATUS_ BOOST_WINAPI_WINAPI_CC
BCryptOpenAlgorithmProvider(boost::winapi::BCRYPT_ALG_HANDLE_ * phAlgorithm,boost::winapi::LPCWSTR_ pszAlgId,boost::winapi::LPCWSTR_ pszImplementation,boost::winapi::DWORD_ dwFlags)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
BCryptGenRandom(boost::winapi::BCRYPT_ALG_HANDLE_ hAlgorithm,boost::winapi::PUCHAR_ pbBuffer,boost::winapi::ULONG_ cbBuffer,boost::winapi::ULONG_ dwFlags)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
BCryptCloseAlgorithmProvider(boost::winapi::BCRYPT_ALG_HANDLE_ hAlgorithm,boost::winapi::ULONG_ dwFlags)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
expectations_capable()146 bool expectations_capable()
147 {
148 return true;
149 }
150
expectations_met()151 bool expectations_met()
152 {
153 return getrandom_next_result.empty();
154 }
155
expect_next_call_success(bool success)156 void expect_next_call_success(bool success)
157 {
158 getrandom_next_result.push_back(success);
159 }
160
provider_acquires_context()161 bool provider_acquires_context()
162 {
163 return false;
164 }
165
mock_getrandom(void * buffer,size_t length,unsigned int flags)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
expectations_capable()190 bool expectations_capable()
191 {
192 return true;
193 }
194
expectations_met()195 bool expectations_met()
196 {
197 return getentropy_next_result.empty();
198 }
199
expect_next_call_success(bool success)200 void expect_next_call_success(bool success)
201 {
202 getentropy_next_result.push_back(success ? 0 : -1);
203 }
204
provider_acquires_context()205 bool provider_acquires_context()
206 {
207 return false;
208 }
209
getentropy(void * buffer,size_t length)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
expectations_capable()227 bool expectations_capable()
228 {
229 return true;
230 }
231
expectations_met()232 bool expectations_met()
233 {
234 return posix_next_result.empty();
235 }
236
expect_next_call_success(bool success)237 void expect_next_call_success(bool success)
238 {
239 posix_next_result.push_back(success);
240 }
241
provider_acquires_context()242 bool provider_acquires_context()
243 {
244 return true;
245 }
246
mockopen(const char * fname,int flags)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
mockread(int fd,void * buf,size_t siz)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