1/*
2 Copyright (c) 2007-2019 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28*/
29#include "testutil_unity.hpp"
30
31#include <stdlib.h>
32#include <string.h>
33
34#ifdef _WIN32
35#include <direct.h>
36#else
37#include <unistd.h>
38#endif
39
40int test_assert_success_message_errno_helper (int rc_,
41 const char *msg_,
42 const char *expr_,
43 int line_)
44{
45 if (rc_ == -1) {
46 char buffer[512];
47 buffer[sizeof (buffer) - 1] =
48 0; // to ensure defined behavior with VC++ <= 2013
49 snprintf (buffer, sizeof (buffer) - 1,
50 "%s failed%s%s%s, errno = %i (%s)", expr_,
51 msg_ ? " (additional info: " : "", msg_ ? msg_ : "",
52 msg_ ? ")" : "", zmq_errno (), zmq_strerror (zmq_errno ()));
53 UNITY_TEST_FAIL (line_, buffer);
54 }
55 return rc_;
56}
57
58int test_assert_success_message_raw_errno_helper (int rc_,
59 const char *msg_,
60 const char *expr_,
61 int line_)
62{
63 if (rc_ == -1) {
64#if defined ZMQ_HAVE_WINDOWS
65 int current_errno = WSAGetLastError ();
66#else
67 int current_errno = errno;
68#endif
69
70 char buffer[512];
71 buffer[sizeof (buffer) - 1] =
72 0; // to ensure defined behavior with VC++ <= 2013
73 snprintf (buffer, sizeof (buffer) - 1, "%s failed%s%s%s, errno = %i",
74 expr_, msg_ ? " (additional info: " : "", msg_ ? msg_ : "",
75 msg_ ? ")" : "", current_errno);
76 UNITY_TEST_FAIL (line_, buffer);
77 }
78 return rc_;
79}
80
81int test_assert_failure_message_raw_errno_helper (
82 int rc_, int expected_errno_, const char *msg_, const char *expr_, int line_)
83{
84 char buffer[512];
85 buffer[sizeof (buffer) - 1] =
86 0; // to ensure defined behavior with VC++ <= 2013
87 if (rc_ != -1) {
88 snprintf (buffer, sizeof (buffer) - 1,
89 "%s was unexpectedly successful%s%s%s, expected "
90 "errno = %i, actual return value = %i",
91 expr_, msg_ ? " (additional info: " : "", msg_ ? msg_ : "",
92 msg_ ? ")" : "", expected_errno_, rc_);
93 UNITY_TEST_FAIL (line_, buffer);
94 } else {
95#if defined ZMQ_HAVE_WINDOWS
96 int current_errno = WSAGetLastError ();
97#else
98 int current_errno = errno;
99#endif
100 if (current_errno != expected_errno_) {
101 snprintf (buffer, sizeof (buffer) - 1,
102 "%s failed with an unexpected error%s%s%s, expected "
103 "errno = %i, actual errno = %i",
104 expr_, msg_ ? " (additional info: " : "",
105 msg_ ? msg_ : "", msg_ ? ")" : "", expected_errno_,
106 current_errno);
107 UNITY_TEST_FAIL (line_, buffer);
108 }
109 }
110 return rc_;
111}
112
113void send_string_expect_success (void *socket_, const char *str_, int flags_)
114{
115 const size_t len = str_ ? strlen (str_) : 0;
116 const int rc = zmq_send (socket_, str_, len, flags_);
117 TEST_ASSERT_EQUAL_INT ((int) len, rc);
118}
119
120void recv_string_expect_success (void *socket_, const char *str_, int flags_)
121{
122 const size_t len = str_ ? strlen (str_) : 0;
123 char buffer[255];
124 TEST_ASSERT_LESS_OR_EQUAL_MESSAGE (sizeof (buffer), len,
125 "recv_string_expect_success cannot be "
126 "used for strings longer than 255 "
127 "characters");
128
129 const int rc = TEST_ASSERT_SUCCESS_ERRNO (
130 zmq_recv (socket_, buffer, sizeof (buffer), flags_));
131 TEST_ASSERT_EQUAL_INT ((int) len, rc);
132 if (str_)
133 TEST_ASSERT_EQUAL_STRING_LEN (str_, buffer, len);
134}
135
136static void *internal_manage_test_context (bool init_, bool clear_)
137{
138 static void *test_context = NULL;
139 if (clear_) {
140 TEST_ASSERT_NOT_NULL (test_context);
141 TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_term (test_context));
142 test_context = NULL;
143 } else {
144 if (init_) {
145 TEST_ASSERT_NULL (test_context);
146 test_context = zmq_ctx_new ();
147 TEST_ASSERT_NOT_NULL (test_context);
148 }
149 }
150 return test_context;
151}
152
153static void internal_manage_test_sockets (void *socket_, bool add_)
154{
155 static void *test_sockets[MAX_TEST_SOCKETS];
156 static size_t test_socket_count = 0;
157 if (!socket_) {
158 TEST_ASSERT_FALSE (add_);
159
160 // force-close all sockets
161 if (test_socket_count) {
162 for (size_t i = 0; i < test_socket_count; ++i) {
163 close_zero_linger (test_sockets[i]);
164 }
165 fprintf (stderr,
166 "WARNING: Forced closure of %i sockets, this is an "
167 "implementation error unless the test case failed\n",
168 static_cast<int> (test_socket_count));
169 test_socket_count = 0;
170 }
171 } else {
172 if (add_) {
173 ++test_socket_count;
174 TEST_ASSERT_LESS_THAN_MESSAGE (MAX_TEST_SOCKETS, test_socket_count,
175 "MAX_TEST_SOCKETS must be "
176 "increased, or you cannot use the "
177 "test context");
178 test_sockets[test_socket_count - 1] = socket_;
179 } else {
180 bool found = false;
181 for (size_t i = 0; i < test_socket_count; ++i) {
182 if (test_sockets[i] == socket_) {
183 found = true;
184 }
185 if (found) {
186 if (i < test_socket_count)
187 test_sockets[i] = test_sockets[i + 1];
188 }
189 }
190 TEST_ASSERT_TRUE_MESSAGE (found,
191 "Attempted to close a socket that was "
192 "not created by test_context_socket");
193 --test_socket_count;
194 }
195 }
196}
197
198void setup_test_context ()
199{
200 internal_manage_test_context (true, false);
201}
202
203void *get_test_context ()
204{
205 return internal_manage_test_context (false, false);
206}
207
208void teardown_test_context ()
209{
210 // this condition allows an explicit call to teardown_test_context from a
211 // test. if this is never used, it should probably be removed, to detect
212 // misuses
213 if (get_test_context ()) {
214 internal_manage_test_sockets (NULL, false);
215 internal_manage_test_context (false, true);
216 }
217}
218
219void *test_context_socket (int type_)
220{
221 void *const socket = zmq_socket (get_test_context (), type_);
222 TEST_ASSERT_NOT_NULL (socket);
223 internal_manage_test_sockets (socket, true);
224 return socket;
225}
226
227void *test_context_socket_close (void *socket_)
228{
229 TEST_ASSERT_SUCCESS_ERRNO (zmq_close (socket_));
230 internal_manage_test_sockets (socket_, false);
231 return socket_;
232}
233
234void *test_context_socket_close_zero_linger (void *socket_)
235{
236 const int linger = 0;
237 int rc = zmq_setsockopt (socket_, ZMQ_LINGER, &linger, sizeof (linger));
238 TEST_ASSERT_TRUE (rc == 0 || zmq_errno () == ETERM);
239 return test_context_socket_close (socket_);
240}
241
242void test_bind (void *socket_,
243 const char *bind_address_,
244 char *my_endpoint_,
245 size_t len_)
246{
247 TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (socket_, bind_address_));
248 TEST_ASSERT_SUCCESS_ERRNO (
249 zmq_getsockopt (socket_, ZMQ_LAST_ENDPOINT, my_endpoint_, &len_));
250}
251
252void bind_loopback (void *socket_, int ipv6_, char *my_endpoint_, size_t len_)
253{
254 if (ipv6_ && !is_ipv6_available ()) {
255 TEST_IGNORE_MESSAGE ("ipv6 is not available");
256 }
257
258 TEST_ASSERT_SUCCESS_ERRNO (
259 zmq_setsockopt (socket_, ZMQ_IPV6, &ipv6_, sizeof (int)));
260
261 test_bind (socket_, ipv6_ ? "tcp://[::1]:*" : "tcp://127.0.0.1:*",
262 my_endpoint_, len_);
263}
264
265void bind_loopback_ipv4 (void *socket_, char *my_endpoint_, size_t len_)
266{
267 bind_loopback (socket_, false, my_endpoint_, len_);
268}
269
270void bind_loopback_ipv6 (void *socket_, char *my_endpoint_, size_t len_)
271{
272 bind_loopback (socket_, true, my_endpoint_, len_);
273}
274
275void bind_loopback_ipc (void *socket_, char *my_endpoint_, size_t len_)
276{
277 if (!zmq_has ("ipc")) {
278 TEST_IGNORE_MESSAGE ("ipc is not available");
279 }
280
281 test_bind (socket_, "ipc://*", my_endpoint_, len_);
282}
283
284void bind_loopback_tipc (void *socket_, char *my_endpoint_, size_t len_)
285{
286 if (!is_tipc_available ()) {
287 TEST_IGNORE_MESSAGE ("tipc is not available");
288 }
289
290 test_bind (socket_, "tipc://<*>", my_endpoint_, len_);
291}
292
293#if defined(ZMQ_HAVE_IPC) && !defined(ZMQ_HAVE_GNU)
294void make_random_ipc_endpoint (char *out_endpoint_)
295{
296#ifdef ZMQ_HAVE_WINDOWS
297 char random_file[MAX_PATH];
298
299 {
300 const errno_t rc = tmpnam_s (random_file);
301 TEST_ASSERT_EQUAL (0, rc);
302 }
303
304 // TODO or use CreateDirectoryA and specify permissions?
305 const int rc = _mkdir (random_file);
306 TEST_ASSERT_EQUAL (0, rc);
307
308 strcat (random_file, "/ipc");
309
310#else
311 char random_file[16];
312 strcpy (random_file, "tmpXXXXXX");
313
314#ifdef HAVE_MKDTEMP
315 TEST_ASSERT_TRUE (mkdtemp (random_file));
316 strcat (random_file, "/ipc");
317#else
318 int fd = mkstemp (random_file);
319 TEST_ASSERT_TRUE (fd != -1);
320 close (fd);
321#endif
322#endif
323
324 strcpy (out_endpoint_, "ipc://");
325 strcat (out_endpoint_, random_file);
326}
327#endif
328