Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include "zeromqrecv.h"
10 :
11 : #include <stdio.h> // printf() & co
12 : #include <time.h> // time()
13 : #include <unistd.h> // usleep()
14 :
15 : #include <kdbio/uv.h> // elektraIoUvNew()
16 : #include <kdbioplugin.h> // ElektraIoPluginSetBinding
17 :
18 : #include <tests.h>
19 : #include <tests_plugin.h>
20 :
21 : #include <uv.h>
22 :
23 : /** zmq context for tests */
24 : void * context;
25 :
26 : /** time in microseconds to wait until zmq connections are established and sending & receiving works */
27 : #define TIME_SETTLE_US (1000 * 1000)
28 :
29 : /** time (100ms in microseconds) before a new socket is created. leaves the system some after binding a socket again */
30 : #define TIME_HOLDOFF (100 * 1000)
31 :
32 : /** timeout for tests in seconds */
33 : #define TEST_TIMEOUT 10
34 :
35 : Key * test_callbackKey;
36 : uv_loop_t * test_callbackLoop;
37 : int test_incompleteMessageTimeout;
38 :
39 : /**
40 : * @internal
41 : * Create publisher socket for tests.
42 : *
43 : * @return new socket
44 : */
45 4 : static void * createTestSocket (void)
46 : {
47 : // leave the system some time before binding again
48 4 : usleep (TIME_HOLDOFF);
49 :
50 4 : void * pubSocket = zmq_socket (context, ZMQ_PUB);
51 4 : int result = zmq_bind (pubSocket, "tcp://*:6001");
52 4 : if (result != 0)
53 : {
54 0 : yield_error ("zmq_bind failed");
55 0 : printf ("zmq error was: %s\n", zmq_strerror (zmq_errno ()));
56 0 : exit (-1);
57 : }
58 :
59 4 : return pubSocket;
60 : }
61 :
62 : /**
63 : * @internal
64 : * Send a notification over a socket.
65 : *
66 : * @param socket ZeroMq socket
67 : * @param changeType change type
68 : * @param keyName key name
69 : */
70 2 : static void sendTestNotification (void * socket, char * changeType, char * keyName)
71 : {
72 2 : succeed_if (zmq_send (socket, changeType, elektraStrLen (changeType), ZMQ_SNDMORE) != -1, "failed to send change type");
73 2 : succeed_if (zmq_send (socket, keyName, elektraStrLen (keyName), 0) != -1, "failed to send key name");
74 2 : }
75 :
76 : /**
77 : * @internal
78 : * Called by plugin when a notification was received.
79 : * The key is saved to be evaluated by the current test and the event loop is
80 : * stopped.
81 : *
82 : * @see ElektraNotificationCallback (kdbnotificationinternal.h)
83 : *
84 : * @param key changed key
85 : * @param context notification callback context
86 : */
87 2 : static void test_notificationCallback (Key * key, ElektraNotificationCallbackContext * callbackContext ELEKTRA_UNUSED)
88 : {
89 2 : test_callbackKey = key;
90 2 : uv_stop (test_callbackLoop);
91 2 : }
92 :
93 : /**
94 : * Timeout for tests.
95 : *
96 : * Creates a failure and stops the event loop
97 : *
98 : * @param timerOp timer operation
99 : */
100 0 : static void test_timerCallback (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
101 : {
102 0 : yield_error ("timeout exceeded; test failed");
103 0 : uv_stop (test_callbackLoop);
104 0 : }
105 :
106 : /**
107 : * Timeout for incomplete message test.
108 : *
109 : * Creates stops the event loop
110 : *
111 : * @param timerOp timer operation
112 : */
113 2 : static void test_timerCallbackIncomplete (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
114 : {
115 2 : test_incompleteMessageTimeout = 1;
116 2 : uv_stop (test_callbackLoop);
117 2 : }
118 :
119 2 : static void test_commit (uv_loop_t * loop, ElektraIoInterface * binding)
120 : {
121 2 : printf ("test commit notification\n");
122 :
123 2 : KeySet * conf = ksNew (0, KS_END);
124 2 : PLUGIN_OPEN ("zeromqrecv");
125 :
126 2 : void * pubSocket = createTestSocket ();
127 :
128 : // set io binding
129 2 : size_t func = elektraPluginGetFunction (plugin, "setIoBinding");
130 2 : exit_if_fail (func, "could not get function setIoBinding");
131 2 : KeySet * setIoBindingParams =
132 2 : ksNew (1, keyNew ("/ioBinding", KEY_BINARY, KEY_SIZE, sizeof (binding), KEY_VALUE, &binding, KEY_END), KS_END);
133 2 : ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func;
134 2 : setIoBinding (plugin, setIoBindingParams);
135 2 : ksDel (setIoBindingParams);
136 :
137 : // open notification
138 2 : func = elektraPluginGetFunction (plugin, "openNotification");
139 2 : exit_if_fail (func, "could not get function openNotification");
140 2 : KeySet * openNotificationParams = ksNew (2, keyNew ("/callback", KEY_FUNC, test_notificationCallback, KEY_END), KS_END);
141 2 : ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func;
142 2 : openNotification (plugin, openNotificationParams);
143 2 : ksDel (openNotificationParams);
144 :
145 2 : usleep (TIME_SETTLE_US);
146 :
147 2 : char * changeType = "Commit";
148 2 : char * expectedKeyName = "system/foo/bar";
149 2 : sendTestNotification (pubSocket, changeType, expectedKeyName);
150 :
151 2 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (TEST_TIMEOUT * 1000, 1, test_timerCallback, NULL);
152 2 : elektraIoBindingAddTimer (binding, timerOp);
153 :
154 2 : test_callbackKey = NULL;
155 2 : test_callbackLoop = loop;
156 2 : uv_run (loop, UV_RUN_DEFAULT);
157 :
158 2 : succeed_if_same_string (expectedKeyName, keyName (test_callbackKey));
159 :
160 : // close notification
161 2 : func = elektraPluginGetFunction (plugin, "closeNotification");
162 2 : exit_if_fail (func, "could not get function closeNotification");
163 2 : ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func;
164 2 : closeNotification (plugin, NULL);
165 :
166 2 : zmq_close (pubSocket);
167 :
168 2 : elektraIoBindingRemoveTimer (timerOp);
169 2 : elektraFree (timerOp);
170 2 : keyDel (test_callbackKey);
171 2 : PLUGIN_CLOSE ();
172 2 : }
173 :
174 2 : static void test_incompleteMessage (uv_loop_t * loop, ElektraIoInterface * binding)
175 : {
176 2 : printf ("test incomplete message\n");
177 :
178 2 : KeySet * conf = ksNew (0, KS_END);
179 2 : PLUGIN_OPEN ("zeromqrecv");
180 :
181 2 : void * pubSocket = createTestSocket ();
182 :
183 : // set io binding
184 2 : size_t func = elektraPluginGetFunction (plugin, "setIoBinding");
185 2 : exit_if_fail (func, "could not get function setIoBinding");
186 2 : KeySet * setIoBindingParams =
187 2 : ksNew (1, keyNew ("/ioBinding", KEY_BINARY, KEY_SIZE, sizeof (binding), KEY_VALUE, &binding, KEY_END), KS_END);
188 2 : ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func;
189 2 : setIoBinding (plugin, setIoBindingParams);
190 2 : ksDel (setIoBindingParams);
191 :
192 : // open notification
193 2 : func = elektraPluginGetFunction (plugin, "openNotification");
194 2 : exit_if_fail (func, "could not get function openNotification");
195 2 : KeySet * openNotificationParams = ksNew (2, keyNew ("/callback", KEY_FUNC, test_notificationCallback, KEY_END), KS_END);
196 2 : ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func;
197 2 : openNotification (plugin, openNotificationParams);
198 2 : ksDel (openNotificationParams);
199 :
200 2 : usleep (TIME_SETTLE_US);
201 :
202 2 : char * changeType = "KeyChanged";
203 2 : char * expectedKeyName = "system/foo/bar";
204 : // send message parts as standalone messages
205 2 : succeed_if (zmq_send (pubSocket, changeType, elektraStrLen (changeType), 0 /* no ZMQ_SNDMORE here */) != -1,
206 : "failed to send change type");
207 2 : succeed_if (zmq_send (pubSocket, expectedKeyName, elektraStrLen (expectedKeyName), 0) != -1, "failed to send change type");
208 :
209 2 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (TEST_TIMEOUT * 1000, 1, test_timerCallbackIncomplete, NULL);
210 2 : elektraIoBindingAddTimer (binding, timerOp);
211 :
212 2 : test_callbackKey = NULL;
213 2 : test_callbackLoop = loop;
214 2 : test_incompleteMessageTimeout = 0;
215 2 : uv_run (loop, UV_RUN_DEFAULT);
216 :
217 2 : succeed_if (test_incompleteMessageTimeout, "test did not timeout");
218 2 : succeed_if (test_callbackKey == NULL, "should not receive key");
219 :
220 : // close notification
221 2 : func = elektraPluginGetFunction (plugin, "closeNotification");
222 2 : exit_if_fail (func, "could not get function closeNotification");
223 2 : ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func;
224 2 : closeNotification (plugin, NULL);
225 :
226 2 : zmq_close (pubSocket);
227 :
228 2 : elektraIoBindingRemoveTimer (timerOp);
229 2 : elektraFree (timerOp);
230 2 : PLUGIN_CLOSE ();
231 2 : }
232 :
233 2 : int main (int argc, char ** argv)
234 : {
235 2 : printf ("ZEROMQRECV TESTS\n");
236 2 : printf ("================\n\n");
237 :
238 2 : init (argc, argv);
239 :
240 : int major, minor, patch;
241 2 : zmq_version (&major, &minor, &patch);
242 2 : printf ("zeromq version is %d.%d.%d\n", major, minor, patch);
243 :
244 2 : context = zmq_ctx_new ();
245 :
246 2 : uv_loop_t * loop = uv_default_loop ();
247 2 : ElektraIoInterface * binding = elektraIoUvNew (loop);
248 :
249 2 : test_commit (loop, binding);
250 2 : test_incompleteMessage (loop, binding);
251 :
252 2 : print_result ("testmod_zeromqrecv");
253 :
254 2 : elektraIoBindingCleanup (binding);
255 :
256 2 : zmq_ctx_destroy (context);
257 :
258 2 : while (uv_run (loop, UV_RUN_NOWAIT) != 0)
259 : ;
260 : #ifdef HAVE_LIBUV1
261 2 : uv_loop_close (loop);
262 : #elif HAVE_LIBUV0
263 : uv_loop_delete (loop);
264 : #endif
265 2 : return nbError;
266 : }
|