Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Tests for I/O bindings
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : * In these tests `pipe()` is used to create file descriptors that can be used
9 : * for testing readability or writability. Additionally `pipe()` is POSIX
10 : * compliant making the tests more portable.
11 : */
12 :
13 : #include <stdio.h>
14 : #include <stdlib.h>
15 : #include <string.h>
16 : #include <unistd.h>
17 :
18 : #include <tests.h>
19 :
20 : #include "test.h"
21 : #include <kdbio.h>
22 : #include <kdbiotest.h>
23 :
24 : #define FD_CONTROL_INTERVAL 200
25 :
26 : #define FD_BUFFER_TESTDATA "T"
27 : #define FD_BUFFER_TESTDATA_LENGTH 1
28 :
29 : // Indices for array returned by pipe()
30 : #define FD_READ_END 0
31 : #define FD_WRITE_END 1
32 :
33 : ElektraIoTestSuiteStop testStop;
34 :
35 : int testSignalWritableCalled;
36 :
37 : int testSignalReadableCalled;
38 :
39 : int testUpdateEnabledCalled;
40 : int testUpdateEnabledStep;
41 : ElektraIoInterface * testUpdateEnabledBinding;
42 : ElektraIoFdOperation * testUpdateEnabledFdOp;
43 :
44 : int testUpdateFlagsCalled;
45 : int testUpdateFlagsStep;
46 : ElektraIoInterface * testUpdateFlagsBinding;
47 : ElektraIoFdOperation * testUpdateFlagsFdOp;
48 :
49 : int testRemoveCalled;
50 : int testRemoveStep;
51 : ElektraIoInterface * testRemoveBinding;
52 : ElektraIoFdOperation * testRemoveFdOp;
53 :
54 0 : static void testFdBasicsCallback (ElektraIoFdOperation * fdOp ELEKTRA_UNUSED, int flags ELEKTRA_UNUSED)
55 : {
56 0 : yield_error ("should not be called");
57 0 : }
58 :
59 6 : static void testFdBasics (ElektraIoTestSuiteCreateBinding createBinding)
60 : {
61 : int fds[2];
62 6 : if (pipe (fds) == -1)
63 : {
64 0 : yield_error ("pipe() failed");
65 0 : return;
66 : }
67 :
68 6 : ElektraIoFdOperation * fdOp = elektraIoNewFdOperation (fds[FD_READ_END], ELEKTRA_IO_WRITABLE, 0, testFdBasicsCallback, NULL);
69 :
70 6 : ElektraIoInterface * binding = createBinding ();
71 6 : succeed_if (elektraIoBindingAddFd (binding, fdOp), "addFd did not succeed");
72 6 : succeed_if (elektraIoBindingAddFd (binding, fdOp) == 0, "addFd: should not be able to reassign operation to a binding");
73 :
74 6 : elektraIoFdSetEnabled (fdOp, 1);
75 6 : succeed_if (elektraIoBindingUpdateFd (fdOp), "updateFd did not succeed");
76 :
77 6 : succeed_if (elektraIoBindingRemoveFd (fdOp), "removeFd did not succeed");
78 :
79 6 : succeed_if (elektraIoBindingAddFd (binding, fdOp), "addFd: should be able to assign operation after removal");
80 6 : succeed_if (elektraIoBindingRemoveFd (fdOp), "removeFd did not succeed");
81 6 : elektraIoBindingCleanup (binding);
82 6 : elektraFree (fdOp);
83 : }
84 :
85 0 : static void testFdShouldSignalXControl (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
86 : {
87 0 : yield_error ("timeout; test failed");
88 0 : testStop ();
89 0 : }
90 :
91 6 : static void testFdShouldSignalWritableProbe (ElektraIoFdOperation * fdOp ELEKTRA_UNUSED, int flags)
92 : {
93 6 : succeed_if (flags & ELEKTRA_IO_WRITABLE, "flags does not contain ELEKTRA_IO_WRITABLE");
94 6 : testSignalWritableCalled = 1;
95 6 : testStop ();
96 6 : }
97 :
98 6 : static void testFdShouldSignalWritable (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start,
99 : ElektraIoTestSuiteStop stop)
100 : {
101 : int fds[2];
102 6 : if (pipe (fds) == -1)
103 : {
104 0 : yield_error ("pipe() failed");
105 0 : return;
106 : }
107 :
108 6 : ElektraIoFdOperation * fdOp =
109 6 : elektraIoNewFdOperation (fds[FD_WRITE_END], ELEKTRA_IO_WRITABLE, 1, testFdShouldSignalWritableProbe, NULL);
110 :
111 6 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (FD_CONTROL_INTERVAL, 1, testFdShouldSignalXControl, NULL);
112 :
113 6 : ElektraIoInterface * binding = createBinding ();
114 6 : elektraIoBindingAddFd (binding, fdOp);
115 6 : elektraIoBindingAddTimer (binding, timerOp);
116 :
117 6 : testSignalWritableCalled = 0;
118 6 : testStop = stop;
119 :
120 6 : start ();
121 :
122 6 : succeed_if (testSignalWritableCalled, "callback was not called");
123 :
124 6 : elektraIoBindingRemoveFd (fdOp);
125 6 : elektraIoBindingRemoveTimer (timerOp);
126 6 : elektraIoBindingCleanup (binding);
127 6 : elektraFree (fdOp);
128 6 : elektraFree (timerOp);
129 6 : close (fds[FD_READ_END]);
130 6 : close (fds[FD_WRITE_END]);
131 : }
132 :
133 6 : static void testFdShouldSignalReadableProbe (ElektraIoFdOperation * fdOp, int flags)
134 : {
135 : char buffer[FD_BUFFER_TESTDATA_LENGTH];
136 6 : succeed_if (read (elektraIoFdGetFd (fdOp), &buffer, FD_BUFFER_TESTDATA_LENGTH) == FD_BUFFER_TESTDATA_LENGTH, "read failed");
137 6 : succeed_if (strncmp (buffer, FD_BUFFER_TESTDATA, FD_BUFFER_TESTDATA_LENGTH) == 0, "did not read correct data");
138 :
139 6 : succeed_if (flags & ELEKTRA_IO_READABLE, "flags does not contain ELEKTRA_IO_READABLE");
140 6 : testSignalReadableCalled = 1;
141 6 : testStop ();
142 6 : }
143 :
144 6 : static void testFdShouldSignalReadable (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start,
145 : ElektraIoTestSuiteStop stop)
146 : {
147 : int fds[2];
148 6 : if (pipe (fds) == -1)
149 : {
150 0 : yield_error ("pipe() failed");
151 0 : return;
152 : }
153 6 : char * buffer = FD_BUFFER_TESTDATA;
154 6 : succeed_if (write (fds[FD_WRITE_END], buffer, FD_BUFFER_TESTDATA_LENGTH) == FD_BUFFER_TESTDATA_LENGTH, "write failed");
155 :
156 6 : ElektraIoFdOperation * fdOp =
157 6 : elektraIoNewFdOperation (fds[FD_READ_END], ELEKTRA_IO_READABLE, 1, testFdShouldSignalReadableProbe, NULL);
158 :
159 6 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (FD_CONTROL_INTERVAL, 1, testFdShouldSignalXControl, NULL);
160 :
161 6 : ElektraIoInterface * binding = createBinding ();
162 6 : elektraIoBindingAddFd (binding, fdOp);
163 6 : elektraIoBindingAddTimer (binding, timerOp);
164 :
165 6 : testSignalReadableCalled = 0;
166 6 : testStop = stop;
167 :
168 6 : start ();
169 :
170 6 : succeed_if (testSignalReadableCalled, "callback was not called");
171 :
172 6 : elektraIoBindingRemoveFd (fdOp);
173 6 : elektraIoBindingRemoveTimer (timerOp);
174 6 : elektraIoBindingCleanup (binding);
175 6 : elektraFree (fdOp);
176 6 : elektraFree (timerOp);
177 6 : close (fds[FD_READ_END]);
178 6 : close (fds[FD_WRITE_END]);
179 : }
180 :
181 6 : static void testFdShouldUpdateEnabledControl (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
182 : {
183 6 : switch (testUpdateEnabledStep)
184 : {
185 : case 0:
186 6 : elektraIoFdSetEnabled (testUpdateEnabledFdOp, 1);
187 6 : elektraIoBindingUpdateFd (testUpdateEnabledFdOp);
188 6 : break;
189 : case 1:
190 0 : yield_error ("timeout; test failed");
191 0 : testStop ();
192 0 : break;
193 : }
194 6 : testUpdateEnabledStep++;
195 6 : }
196 :
197 6 : static void testFdShouldUpdateEnabledProbe (ElektraIoFdOperation * fdOp ELEKTRA_UNUSED, int flags ELEKTRA_UNUSED)
198 : {
199 6 : succeed_if (testUpdateEnabledStep != 0, "callback called before enabeld was updated");
200 6 : testUpdateEnabledCalled = 1;
201 6 : testStop ();
202 6 : }
203 :
204 6 : static void testFdShouldUpdateEnabled (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start,
205 : ElektraIoTestSuiteStop stop)
206 : {
207 : int fds[2];
208 6 : if (pipe (fds) == -1)
209 : {
210 0 : yield_error ("pipe() failed");
211 0 : return;
212 : }
213 :
214 6 : ElektraIoFdOperation * fdOp = elektraIoNewFdOperation (fds[FD_WRITE_END], ELEKTRA_IO_WRITABLE,
215 : 0, // gets enabled by control timer
216 : testFdShouldUpdateEnabledProbe, NULL);
217 :
218 6 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (FD_CONTROL_INTERVAL, 1, testFdShouldUpdateEnabledControl, NULL);
219 :
220 6 : ElektraIoInterface * binding = createBinding ();
221 6 : elektraIoBindingAddFd (binding, fdOp);
222 6 : elektraIoBindingAddTimer (binding, timerOp);
223 :
224 6 : testUpdateEnabledStep = 0;
225 6 : testUpdateEnabledBinding = binding;
226 6 : testUpdateEnabledFdOp = fdOp;
227 6 : testUpdateEnabledCalled = 0;
228 6 : testStop = stop;
229 :
230 6 : start ();
231 :
232 6 : succeed_if (testUpdateEnabledCalled, "callback was not called");
233 :
234 6 : elektraIoBindingRemoveFd (fdOp);
235 6 : elektraIoBindingRemoveTimer (timerOp);
236 6 : elektraIoBindingCleanup (binding);
237 6 : elektraFree (fdOp);
238 6 : elektraFree (timerOp);
239 6 : close (fds[FD_READ_END]);
240 6 : close (fds[FD_WRITE_END]);
241 : }
242 :
243 6 : static void testFdShouldUpdateFlagsControl (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
244 : {
245 6 : switch (testUpdateFlagsStep)
246 : {
247 : case 0:
248 6 : elektraIoFdSetFlags (testUpdateFlagsFdOp, ELEKTRA_IO_WRITABLE);
249 6 : elektraIoBindingUpdateFd (testUpdateFlagsFdOp);
250 6 : break;
251 : case 1:
252 0 : yield_error ("timeout; test failed");
253 0 : testStop ();
254 0 : break;
255 : }
256 6 : testUpdateFlagsStep++;
257 6 : }
258 :
259 6 : static void testFdShouldUpdateFlagsProbe (ElektraIoFdOperation * fdOp ELEKTRA_UNUSED, int flags ELEKTRA_UNUSED)
260 : {
261 6 : succeed_if (testUpdateFlagsStep != 0, "callback called before flags were updated");
262 6 : testUpdateFlagsCalled = 1;
263 6 : testStop ();
264 6 : }
265 :
266 6 : static void testFdShouldUpdateFlags (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start,
267 : ElektraIoTestSuiteStop stop)
268 : {
269 : int fds[2];
270 6 : if (pipe (fds) == -1)
271 : {
272 0 : yield_error ("pipe() failed");
273 0 : return;
274 : }
275 :
276 6 : ElektraIoFdOperation * fdOp = elektraIoNewFdOperation (fds[FD_WRITE_END],
277 : ELEKTRA_IO_READABLE, // gets changed by control timer
278 : 1, testFdShouldUpdateFlagsProbe, NULL);
279 :
280 6 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (FD_CONTROL_INTERVAL, 1, testFdShouldUpdateFlagsControl, NULL);
281 :
282 6 : ElektraIoInterface * binding = createBinding ();
283 6 : elektraIoBindingAddFd (binding, fdOp);
284 6 : elektraIoBindingAddTimer (binding, timerOp);
285 :
286 6 : testUpdateFlagsStep = 0;
287 6 : testUpdateFlagsBinding = binding;
288 6 : testUpdateFlagsFdOp = fdOp;
289 6 : testUpdateFlagsCalled = 0;
290 6 : testStop = stop;
291 :
292 6 : start ();
293 :
294 6 : succeed_if (testUpdateFlagsCalled, "callback was not called");
295 :
296 6 : elektraIoBindingRemoveFd (fdOp);
297 6 : elektraIoBindingRemoveTimer (timerOp);
298 6 : elektraIoBindingCleanup (binding);
299 6 : elektraFree (fdOp);
300 6 : elektraFree (timerOp);
301 6 : close (fds[FD_READ_END]);
302 6 : close (fds[FD_WRITE_END]);
303 : }
304 :
305 12 : static void testFdShouldRemoveControl (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
306 : {
307 12 : switch (testRemoveStep)
308 : {
309 : case 0:
310 6 : elektraIoBindingRemoveFd (testRemoveFdOp);
311 6 : char * buffer = "this should not fire the callback";
312 6 : succeed_if (write (elektraIoFdGetFd (testRemoveFdOp), buffer, FD_BUFFER_TESTDATA_LENGTH) == FD_BUFFER_TESTDATA_LENGTH,
313 : "write failed");
314 : break;
315 : case 1:
316 6 : testStop ();
317 6 : break;
318 : }
319 12 : testRemoveStep++;
320 12 : }
321 :
322 0 : static void testFdShouldRemoveProbe (ElektraIoFdOperation * fdOp ELEKTRA_UNUSED, int flags ELEKTRA_UNUSED)
323 : {
324 0 : succeed_if (testRemoveStep == 0, "callback called after fd was removed");
325 0 : testRemoveCalled = 1;
326 0 : testStop ();
327 0 : }
328 :
329 6 : static void testFdShouldRemove (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start, ElektraIoTestSuiteStop stop)
330 : {
331 : int fds[2];
332 6 : if (pipe (fds) == -1)
333 : {
334 0 : yield_error ("pipe() failed");
335 0 : return;
336 : }
337 :
338 6 : ElektraIoFdOperation * fdOp = elektraIoNewFdOperation (fds[FD_WRITE_END],
339 : ELEKTRA_IO_READABLE, // gets changed by control timer
340 : 1, testFdShouldRemoveProbe, NULL);
341 :
342 6 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (FD_CONTROL_INTERVAL, 1, testFdShouldRemoveControl, NULL);
343 :
344 6 : ElektraIoInterface * binding = createBinding ();
345 6 : elektraIoBindingAddFd (binding, fdOp);
346 6 : elektraIoBindingAddTimer (binding, timerOp);
347 :
348 6 : testRemoveStep = 0;
349 6 : testRemoveBinding = binding;
350 6 : testRemoveFdOp = fdOp;
351 6 : testRemoveCalled = 0;
352 6 : testStop = stop;
353 :
354 6 : start ();
355 :
356 6 : succeed_if (testRemoveCalled == 0, "callback was called");
357 :
358 6 : if (testRemoveStep == 0)
359 : {
360 0 : elektraIoBindingRemoveFd (fdOp);
361 : }
362 6 : elektraIoBindingRemoveTimer (timerOp);
363 6 : elektraIoBindingCleanup (binding);
364 6 : elektraFree (fdOp);
365 6 : elektraFree (timerOp);
366 6 : close (fds[FD_READ_END]);
367 6 : close (fds[FD_WRITE_END]);
368 : }
369 :
370 : /**
371 : * Test fd functions of the I/O binding returned by createBinding.
372 : * Requires the following operations: Fd, Timer
373 : *
374 : * @param createBinding binding creation function
375 : * @param start starts I/O operations
376 : * @param stop stops I/O operations
377 : */
378 6 : void elektraIoTestSuiteFd (ElektraIoTestSuiteCreateBinding createBinding, ElektraIoTestSuiteStart start, ElektraIoTestSuiteStop stop)
379 : {
380 6 : printf ("test file descriptor\n");
381 :
382 6 : testFdBasics (createBinding);
383 :
384 6 : testFdShouldSignalWritable (createBinding, start, stop);
385 :
386 6 : testFdShouldSignalReadable (createBinding, start, stop);
387 :
388 6 : testFdShouldUpdateEnabled (createBinding, start, stop);
389 :
390 6 : testFdShouldUpdateFlags (createBinding, start, stop);
391 :
392 6 : testFdShouldRemove (createBinding, start, stop);
393 6 : }
|