Commit | Line | Data |
---|---|---|
8c15b8c7 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | */ | |
cd9ddaee RP |
20 | /** @file |
21 | * | |
7e586023 | 22 | * (c) Copyright 2007-2009 by Intra2net AG |
cd9ddaee RP |
23 | */ |
24 | ||
cbcb9570 | 25 | //#define NOISEDEBUG |
cd9ddaee RP |
26 | |
27 | #include <string> | |
28 | #include <iostream> | |
29 | #include <iomanip> | |
30 | #include <vector> | |
31 | ||
32 | #include <cppunit/extensions/TestFactoryRegistry.h> | |
33 | #include <cppunit/ui/text/TestRunner.h> | |
34 | #include <cppunit/extensions/HelperMacros.h> | |
35 | ||
4f6228c3 RP |
36 | #include <async_io.hpp> |
37 | #include <async_pipe.hpp> | |
38 | #include <async_process.hpp> | |
39 | #include <async_timer.hpp> | |
40 | #include <async_callout.hpp> | |
41 | #include <async_socket.hpp> | |
7e586023 RP |
42 | #include <asyncio_system_tools.hpp> |
43 | #include <asyncio_containerfunc.hpp> | |
cd9ddaee RP |
44 | #include <boost/signal.hpp> |
45 | #include <boost/bind.hpp> | |
46 | #include <boost/random.hpp> | |
47 | ||
48 | ||
49 | #ifdef NOISEDEBUG | |
50 | #define DOUT(msg) std::cout << msg << std::endl | |
51 | #else | |
52 | #define DOUT(msg) do {} while (0) | |
53 | #endif | |
54 | ||
55 | ||
56 | using namespace I2n; | |
aba4c34d | 57 | using namespace AsyncIo; |
cd9ddaee RP |
58 | |
59 | ||
60 | using namespace CppUnit; | |
61 | ||
62 | namespace { | |
63 | ||
64 | ||
65 | struct Counter | |
66 | { | |
67 | int value; | |
7e586023 RP |
68 | |
69 | ||
cd9ddaee | 70 | Counter() : value(0) { DOUT("Counter construct");} |
7e586023 | 71 | |
cd9ddaee | 72 | void reset() { value=0;} |
7e586023 | 73 | |
cd9ddaee RP |
74 | void advance() |
75 | { | |
76 | DOUT(" advance called"); | |
77 | ++value; | |
78 | } | |
7e586023 | 79 | |
cd9ddaee RP |
80 | int operator()() |
81 | { | |
82 | DOUT(" () called"); | |
83 | return ++value; | |
84 | } | |
85 | }; // eo struct Counter | |
86 | ||
87 | ||
88 | class TestTimer : public TimerBase | |
89 | { | |
90 | public: | |
7e586023 | 91 | |
cd9ddaee RP |
92 | TestTimer() |
93 | : m_counter(0u) | |
94 | { | |
95 | } // eo TestTimer() | |
7e586023 RP |
96 | |
97 | ||
cd9ddaee RP |
98 | void setDelta(long msec) |
99 | { | |
100 | setDeltaWhenTime(0,msec); | |
101 | activate(); | |
102 | } // eo setDelta(long) | |
7e586023 RP |
103 | |
104 | ||
cd9ddaee | 105 | unsigned m_counter; |
7e586023 | 106 | |
cd9ddaee | 107 | protected: |
7e586023 | 108 | |
cd9ddaee RP |
109 | virtual void execute() |
110 | { | |
111 | ++m_counter; | |
112 | } // eo execute() | |
7e586023 | 113 | |
cd9ddaee RP |
114 | }; // eo class TestTimer |
115 | ||
116 | ||
117 | ||
118 | struct ReceivedData | |
119 | { | |
120 | std::vector<std::string> m_received_vector; | |
121 | std::string m_received_string; | |
7e586023 | 122 | |
cd9ddaee RP |
123 | unsigned long m_count_lines; |
124 | unsigned long m_data_size; | |
7e586023 RP |
125 | |
126 | ||
cd9ddaee RP |
127 | void resetReceivedData() |
128 | { | |
129 | m_received_vector.clear(); | |
130 | m_received_string.clear(); | |
131 | m_count_lines= 0uL; | |
132 | m_data_size= 0uL; | |
133 | } // eo reset | |
134 | ||
135 | ||
136 | void receiveData(const std::string& data) | |
137 | { | |
138 | m_received_vector.push_back(data); | |
139 | m_received_string.append(data); | |
140 | ++m_count_lines; | |
141 | m_data_size += data.size(); | |
142 | } // eo receiveData(const std::string&) | |
7e586023 | 143 | |
cd9ddaee RP |
144 | }; // eo struct ReceivedData |
145 | ||
146 | ||
147 | ||
148 | class TestPipe : public SimplePipe, public ReceivedData | |
149 | { | |
150 | public: | |
151 | TestPipe() | |
152 | : SimplePipe() | |
153 | { | |
154 | signal_received_string.connect( boost::bind(&TestPipe::receiveData, this, _1) ); | |
155 | } // eo TestPipe() | |
156 | ||
157 | ||
7e586023 | 158 | |
cd9ddaee | 159 | protected: |
7e586023 | 160 | |
cd9ddaee RP |
161 | }; // eo class TestPipe |
162 | ||
163 | ||
164 | class TestIO : public SimpleIO2, public ReceivedData | |
165 | { | |
166 | public: | |
167 | TestIO() | |
168 | { | |
169 | signal_received_string.connect( boost::bind(&TestIO::receiveData, this, _1) ); | |
170 | } | |
171 | ||
172 | }; // eo TestIO | |
173 | ||
174 | ||
175 | class TestProcess : public ProcessImplementation, public ReceivedData | |
176 | { | |
177 | public: | |
178 | TestProcess( | |
179 | const std::string& path, | |
180 | const std::vector<std::string>& args= std::vector<std::string>() ) | |
181 | : ProcessImplementation(path,args) | |
182 | { | |
183 | m_signal_read.connect( boost::bind(&TestProcess::slotReceivedData, this) ); | |
184 | } // eo Testprocess(const std::string&, const std::vector<std::string>&) | |
7e586023 | 185 | |
cd9ddaee RP |
186 | TestProcess( |
187 | const std::string& path, | |
188 | const std::string& arg1 ) | |
189 | : ProcessImplementation( | |
190 | path, | |
191 | TransientPushBackFiller< std::string,std::vector >()(arg1) | |
192 | ) | |
193 | { | |
194 | m_signal_read.connect( boost::bind(&TestProcess::slotReceivedData, this) ); | |
195 | } // eo Testprocess(const std::string&, const std::vector<std::string>&) | |
7e586023 | 196 | |
cd9ddaee RP |
197 | TestProcess( |
198 | const std::string& path, | |
199 | const std::string& arg1, const std::string& arg2 ) | |
200 | : ProcessImplementation( | |
201 | path, | |
202 | TransientPushBackFiller< std::string, std::vector >()(arg1)(arg2) | |
203 | ) | |
204 | { | |
205 | m_signal_read.connect( boost::bind(&TestProcess::slotReceivedData, this) ); | |
206 | } // eo Testprocess(const std::string&, const std::vector<std::string>&) | |
207 | ||
208 | ||
209 | ||
210 | pid_t pid() { return m_pid; } | |
211 | ||
212 | bool kill( Signal signal) { return ProcessImplementation::kill(signal); } | |
213 | ||
214 | protected: | |
7e586023 | 215 | |
cd9ddaee RP |
216 | void slotReceivedData() |
217 | { | |
218 | receiveData(m_input_buffer); | |
219 | m_input_buffer.clear(); | |
220 | } // eo slotReceivedData() | |
7e586023 | 221 | |
cd9ddaee RP |
222 | }; // eo class TestProcess |
223 | ||
224 | ||
225 | ||
226 | class TestUnixIOSocket | |
227 | : public UnixIOSocket | |
228 | , public ReceivedData | |
229 | { | |
230 | public: | |
7e586023 | 231 | |
cd9ddaee RP |
232 | TestUnixIOSocket() |
233 | : UnixIOSocket() | |
234 | { | |
235 | m_signal_read.connect( boost::bind(&TestUnixIOSocket::slotReceivedData, this) ); | |
236 | } // eo TestUnixIOSocket() | |
7e586023 RP |
237 | |
238 | ||
cd9ddaee RP |
239 | TestUnixIOSocket( |
240 | int fd, const std::string& path, | |
241 | unsigned int peer_pid, unsigned int peer_uid, unsigned int peer_gid | |
242 | ) | |
243 | : UnixIOSocket(fd, path, peer_pid, peer_uid, peer_gid) | |
244 | { | |
245 | m_signal_read.connect( boost::bind(&TestUnixIOSocket::slotReceivedData, this) ); | |
246 | } // eo TestUnixIOSocket() | |
7e586023 RP |
247 | |
248 | ||
cd9ddaee RP |
249 | void sendData(const std::string& data) |
250 | { | |
251 | lowSend(data); | |
252 | } // eo sendData(const std::string&) | |
7e586023 | 253 | |
cd9ddaee | 254 | protected: |
7e586023 | 255 | |
cd9ddaee RP |
256 | void slotReceivedData() |
257 | { | |
258 | receiveData(m_input_buffer); | |
259 | m_input_buffer.clear(); | |
260 | } // eo slotReceivedData() | |
7e586023 | 261 | |
cd9ddaee RP |
262 | }; // eo class TestUnixIOSocket |
263 | ||
264 | typedef boost::shared_ptr< TestUnixIOSocket > TestUnixIOSocketPtr; | |
265 | ||
266 | ||
267 | class UnixIOSocketHolder | |
268 | : public std::vector< UnixIOSocketPtr > | |
269 | { | |
270 | public: | |
7e586023 | 271 | |
cd9ddaee RP |
272 | void operator()(UnixIOSocketPtr ptr) |
273 | { | |
274 | push_back(ptr); | |
275 | } | |
7e586023 | 276 | |
cd9ddaee RP |
277 | void storeBase (IOImplementationPtr ptr) |
278 | { | |
279 | push_back(boost::dynamic_pointer_cast< UnixIOSocket >(ptr) ); | |
280 | } | |
7e586023 | 281 | |
cd9ddaee RP |
282 | void store (UnixIOSocketPtr ptr) |
283 | { | |
284 | push_back(ptr); | |
285 | } | |
7e586023 | 286 | |
cd9ddaee RP |
287 | TestUnixIOSocketPtr get(int idx) |
288 | { | |
289 | return boost::dynamic_pointer_cast< | |
290 | TestUnixIOSocket | |
291 | >( (*this)[idx] ); | |
292 | } | |
293 | }; // eo class UnixIOSocketHolder | |
294 | ||
295 | ||
296 | /// global random generator (from boost lib). | |
297 | boost::mt19937 g_random_gen; | |
298 | ||
299 | ||
300 | /** | |
301 | * generates a string with random characters from a given ASCII subset. | |
302 | * @param len the desired length of the output string | |
303 | * @return a random string of length @a len | |
304 | */ | |
305 | std::string makeRandomAsciiString(std::string::size_type len) | |
306 | { | |
307 | static std::string chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=+-*/%(){}<>.,:;\\"); | |
308 | std::string s; | |
7e586023 | 309 | |
cd9ddaee RP |
310 | boost::uniform_int<> discreter(0, chars.size()-1); |
311 | boost::variate_generator<boost::mt19937&, boost::uniform_int<> > idxgen(g_random_gen, discreter); | |
7e586023 | 312 | |
cd9ddaee | 313 | for(; len-->0;) s+= chars.at( idxgen() ); |
7e586023 | 314 | |
cd9ddaee RP |
315 | return s; |
316 | } // eo makeRandomAsciiString | |
317 | ||
318 | ||
319 | } // eo namespace <anonymous> | |
320 | ||
321 | ||
322 | ||
323 | class TestSimpleioBasics : public TestFixture | |
324 | { | |
325 | CPPUNIT_TEST_SUITE(TestSimpleioBasics); | |
7e586023 | 326 | |
cd9ddaee RP |
327 | CPPUNIT_TEST(EmptyBackendStepCall); |
328 | CPPUNIT_TEST(NonEmptyBackendStepCall); | |
329 | CPPUNIT_TEST(SingleTimerShot); | |
330 | CPPUNIT_TEST(SimpleTimerShot); | |
331 | CPPUNIT_TEST(SimpleTimerShot2); | |
7e586023 | 332 | |
cd9ddaee RP |
333 | CPPUNIT_TEST(EmptyWantTest); |
334 | CPPUNIT_TEST(SimplePipeTest); | |
335 | CPPUNIT_TEST(SimplePipePump); | |
7e586023 | 336 | |
cd9ddaee RP |
337 | CPPUNIT_TEST(SimpleProcessTestBinTrue); |
338 | CPPUNIT_TEST(SimpleProcessTestBinFalse); | |
339 | CPPUNIT_TEST(SimpleProcessTestEcho); | |
340 | CPPUNIT_TEST(SimpleProcessTestStderr); | |
341 | CPPUNIT_TEST(SignaledProcessTermination); | |
7e586023 RP |
342 | |
343 | ||
cd9ddaee RP |
344 | CPPUNIT_TEST(CallOut1); |
345 | CPPUNIT_TEST(CallOut2); | |
346 | CPPUNIT_TEST(RemoveCallOut1); | |
7e586023 | 347 | |
cd9ddaee RP |
348 | CPPUNIT_TEST(FrozenCall_Thaw); |
349 | CPPUNIT_TEST(FrozenCall_Decay); | |
7e586023 RP |
350 | |
351 | ||
cd9ddaee RP |
352 | CPPUNIT_TEST(UnixSockets_ClientServer); |
353 | ||
7e586023 | 354 | |
cd9ddaee RP |
355 | //CPPUNIT_TEST(Dummy); |
356 | CPPUNIT_TEST_SUITE_END(); | |
7e586023 | 357 | |
cd9ddaee | 358 | protected: |
7e586023 | 359 | |
cd9ddaee RP |
360 | Backend *backend; |
361 | std::set<std::string> used_check_files; | |
7e586023 RP |
362 | |
363 | ||
cd9ddaee RP |
364 | template<class Callable> |
365 | bool backendLoopUntil( Callable condition, int maxLoops=100 ) | |
366 | { | |
367 | for (int i=std::max(maxLoops,1); i-->0 && ! condition();) | |
368 | { | |
369 | backend->doOneStep(10); | |
370 | } | |
371 | return condition(); | |
372 | } // eo backendLoopUntil | |
7e586023 RP |
373 | |
374 | ||
cd9ddaee RP |
375 | bool backendStep(int msTimeout= 10, int count=1) |
376 | { | |
377 | bool res= true; | |
378 | for(;count-->0 && res;) | |
379 | { | |
380 | res= backend->doOneStep(msTimeout); | |
381 | } | |
382 | return res; | |
383 | } // eo backendStep | |
7e586023 RP |
384 | |
385 | ||
cd9ddaee RP |
386 | std::string getCheckFilepath(std::string tag) |
387 | { | |
388 | std::string result; | |
389 | result= "__unittest__" + tag + ".dat"; | |
390 | used_check_files.insert(result); | |
391 | return result; | |
392 | } // eo get_check_file_path | |
7e586023 RP |
393 | |
394 | ||
cd9ddaee RP |
395 | void removeCheckFiles() |
396 | { | |
397 | for(std::set<std::string>::iterator it= used_check_files.begin(); | |
398 | it != used_check_files.end(); | |
399 | ++it) | |
400 | { | |
401 | std::string filepath(*it); | |
7e586023 | 402 | if (Utils::FileStat(filepath)) |
cd9ddaee | 403 | { |
7e586023 | 404 | Utils::unlink(filepath); |
cd9ddaee RP |
405 | } |
406 | } | |
407 | used_check_files.clear(); | |
408 | } // eo removeCheckFiles | |
7e586023 RP |
409 | |
410 | ||
411 | ||
cd9ddaee | 412 | public: |
7e586023 | 413 | |
cd9ddaee RP |
414 | void setUp() |
415 | { | |
416 | backend = Backend::getBackend(); | |
417 | installChildHandler(); | |
418 | used_check_files.clear(); | |
419 | } // eo setUp | |
7e586023 RP |
420 | |
421 | ||
cd9ddaee RP |
422 | void tearDown() |
423 | { | |
424 | restoreChildHandler(); | |
425 | removeCheckFiles(); | |
426 | } // eo tearDown | |
7e586023 RP |
427 | |
428 | ||
cd9ddaee RP |
429 | /* |
430 | * the tests: | |
431 | */ | |
7e586023 RP |
432 | |
433 | ||
cd9ddaee RP |
434 | /* |
435 | ** basics: | |
436 | */ | |
7e586023 RP |
437 | |
438 | ||
cd9ddaee RP |
439 | void EmptyBackendStepCall() |
440 | { | |
441 | CPPUNIT_ASSERT( backend ); | |
7e586023 | 442 | |
cd9ddaee RP |
443 | // a backend call without active objects should return false: |
444 | bool result = backend->doOneStep(0); | |
7e586023 | 445 | |
cd9ddaee RP |
446 | CPPUNIT_ASSERT_EQUAL( false, result ); |
447 | } // eo EmptyBackendStepCall | |
7e586023 RP |
448 | |
449 | ||
cd9ddaee RP |
450 | |
451 | void NonEmptyBackendStepCall() | |
452 | { | |
453 | CPPUNIT_ASSERT(backend); | |
7e586023 | 454 | |
cd9ddaee RP |
455 | { |
456 | TestTimer timer; | |
457 | timer.setDelta(10); | |
458 | // with an active object, a step should return true: | |
459 | bool result = backend->doOneStep(0); | |
460 | CPPUNIT_ASSERT_EQUAL( true, result ); | |
461 | // the timer should not be executed: | |
462 | CPPUNIT_ASSERT_EQUAL( 0u, timer.m_counter ); | |
463 | } | |
464 | // now it should return false: | |
465 | bool result = backend->doOneStep(0); | |
466 | CPPUNIT_ASSERT_EQUAL( false, result ); | |
467 | } // eo NonEmptyBackendStepCall | |
7e586023 RP |
468 | |
469 | ||
470 | ||
cd9ddaee RP |
471 | /** |
472 | * check for timer to execute immediatly. | |
473 | */ | |
474 | void SingleTimerShot() | |
475 | { | |
476 | CPPUNIT_ASSERT(backend); | |
7e586023 | 477 | |
cd9ddaee RP |
478 | TestTimer timer; |
479 | timer.setDelta(0); // shot now! | |
7e586023 | 480 | |
cd9ddaee | 481 | bool result = backend->doOneStep(10); |
7e586023 | 482 | |
cd9ddaee RP |
483 | CPPUNIT_ASSERT_EQUAL( true, result ); |
484 | // the timer should be executed once: | |
485 | CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter ); | |
7e586023 | 486 | |
cd9ddaee | 487 | result = backend->doOneStep(0); |
7e586023 | 488 | |
cd9ddaee RP |
489 | CPPUNIT_ASSERT_EQUAL( false, result ); |
490 | // the timer should not be executed again: | |
491 | CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter ); | |
7e586023 | 492 | |
cd9ddaee | 493 | } // eo SingleTimerShot() |
7e586023 RP |
494 | |
495 | ||
496 | ||
cd9ddaee RP |
497 | /** |
498 | * tests a simple timer class to be executed with a timeout. | |
499 | */ | |
500 | void SimpleTimerShot() | |
501 | { | |
502 | bool res; | |
503 | CPPUNIT_ASSERT(backend); | |
7e586023 | 504 | |
cd9ddaee RP |
505 | SimpleTimer timer1; |
506 | Counter counter1; | |
507 | timer1.addAction( boost::bind(&Counter::advance,&counter1) ); | |
508 | CPPUNIT_ASSERT_EQUAL(false, timer1.active()); | |
7e586023 | 509 | |
cd9ddaee RP |
510 | timer1.startTimerMS( 100 ); |
511 | CPPUNIT_ASSERT_EQUAL(true, timer1.active()); | |
7e586023 | 512 | |
cd9ddaee RP |
513 | res=backend->doOneStep( 1000 ); |
514 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
7e586023 | 515 | |
cd9ddaee RP |
516 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); |
517 | } // eo SimpleTimerShot | |
7e586023 RP |
518 | |
519 | ||
520 | ||
cd9ddaee RP |
521 | /** |
522 | * tests 3 timers; after the first was active, disable another and check if the remaining one fires. | |
523 | */ | |
524 | void SimpleTimerShot2() | |
525 | { | |
526 | bool res; | |
527 | CPPUNIT_ASSERT(backend); | |
7e586023 | 528 | |
cd9ddaee RP |
529 | SimpleTimer timer1, timer2, timer3; |
530 | Counter counter1, counter2, counter3; | |
531 | timer1.addAction( boost::bind(&Counter::advance,&counter1) ); | |
532 | timer2.addAction( boost::bind(&Counter::advance,&counter2) ); | |
533 | timer3.addAction( boost::bind(&Counter::advance,&counter3) ); | |
534 | CPPUNIT_ASSERT_EQUAL(false, timer1.active()); | |
535 | CPPUNIT_ASSERT_EQUAL(false, timer2.active()); | |
536 | CPPUNIT_ASSERT_EQUAL(false, timer3.active()); | |
7e586023 | 537 | |
cd9ddaee RP |
538 | timer1.startTimerMS( 100 ); |
539 | timer2.startTimerMS( 500 ); | |
540 | timer3.startTimerMS( 400 ); | |
541 | CPPUNIT_ASSERT_EQUAL(true, timer1.active()); | |
542 | CPPUNIT_ASSERT_EQUAL(true, timer2.active()); | |
543 | CPPUNIT_ASSERT_EQUAL(true, timer3.active()); | |
7e586023 | 544 | |
cd9ddaee RP |
545 | res=backend->doOneStep( 1000 ); |
546 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
7e586023 | 547 | |
cd9ddaee RP |
548 | CPPUNIT_ASSERT_EQUAL(false, timer1.active()); |
549 | CPPUNIT_ASSERT_EQUAL(true, timer2.active()); | |
550 | CPPUNIT_ASSERT_EQUAL(true, timer3.active()); | |
7e586023 | 551 | |
cd9ddaee RP |
552 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); |
553 | CPPUNIT_ASSERT_EQUAL( 0, counter2.value ); | |
554 | CPPUNIT_ASSERT_EQUAL( 0, counter3.value ); | |
7e586023 | 555 | |
cd9ddaee RP |
556 | // now stop the next timer: |
557 | timer3.stopTimer(); | |
558 | CPPUNIT_ASSERT_EQUAL(false, timer3.active()); | |
7e586023 | 559 | |
cd9ddaee RP |
560 | res=backend->doOneStep( 1000 ); |
561 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
7e586023 | 562 | |
cd9ddaee RP |
563 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); |
564 | CPPUNIT_ASSERT_EQUAL( 1, counter2.value ); | |
565 | CPPUNIT_ASSERT_EQUAL( 0, counter3.value ); | |
566 | } // eo SimpleTimerShot2 | |
7e586023 RP |
567 | |
568 | ||
569 | ||
570 | ||
cd9ddaee RP |
571 | /* |
572 | ** I/O tests: | |
573 | */ | |
7e586023 | 574 | |
cd9ddaee RP |
575 | void EmptyWantTest() |
576 | { | |
577 | IOImplementation io; | |
7e586023 | 578 | |
cd9ddaee RP |
579 | CPPUNIT_ASSERT_EQUAL(false, io.wantRead() ); |
580 | CPPUNIT_ASSERT_EQUAL(false, io.wantWrite() ); | |
581 | } // eo EmptyWantTest | |
7e586023 RP |
582 | |
583 | ||
cd9ddaee RP |
584 | /** |
585 | * a simple pipe (and io) test. | |
586 | * | |
587 | * This test basically tests the io framework. | |
588 | * It opens two connected pipes and sends a test string in each direction. | |
589 | * It tests the following functionalities of the base classes: | |
590 | * - set write marks in backend step (enabling direct send of data) | |
591 | * - low send data | |
592 | * - construct and interpret poll() data structures | |
593 | * - receive data | |
594 | * - signal chains for received data | |
595 | * - eof detection | |
596 | * . | |
7e586023 | 597 | * |
cd9ddaee RP |
598 | */ |
599 | void SimplePipeTest() | |
600 | { | |
601 | static const std::string test_string("a test string"); | |
602 | static const std::string test_string2("only another short test string"); | |
7e586023 | 603 | |
cd9ddaee | 604 | CPPUNIT_ASSERT(backend); |
7e586023 | 605 | |
cd9ddaee | 606 | TestPipe pipe1, pipe2; |
7e586023 | 607 | |
cd9ddaee | 608 | bool res= pipe1.makePipe(pipe2); |
7e586023 | 609 | |
cd9ddaee RP |
610 | CPPUNIT_ASSERT_EQUAL(true, res); |
611 | CPPUNIT_ASSERT_EQUAL(true, pipe1.opened()); | |
612 | CPPUNIT_ASSERT_EQUAL(true, pipe2.opened()); | |
7e586023 | 613 | |
cd9ddaee RP |
614 | res= backend->doOneStep(0); |
615 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 616 | |
cd9ddaee | 617 | pipe1.sendString(test_string); |
7e586023 | 618 | |
cd9ddaee RP |
619 | res= backend->doOneStep(0); |
620 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 621 | |
cd9ddaee | 622 | CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string ); |
7e586023 | 623 | |
cd9ddaee | 624 | pipe2.sendString(test_string2); |
7e586023 | 625 | |
cd9ddaee RP |
626 | res= backend->doOneStep(0); |
627 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 628 | |
cd9ddaee | 629 | CPPUNIT_ASSERT_EQUAL( test_string2, pipe1.m_received_string ); |
7e586023 | 630 | |
cd9ddaee RP |
631 | pipe1.close(); |
632 | CPPUNIT_ASSERT_EQUAL(false, pipe1.opened()); | |
7e586023 | 633 | |
cd9ddaee RP |
634 | res= backend->doOneStep(0); |
635 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 636 | |
cd9ddaee RP |
637 | CPPUNIT_ASSERT_EQUAL(true, pipe2.eof()); |
638 | } // eo SimplePipeTest | |
7e586023 RP |
639 | |
640 | ||
641 | ||
cd9ddaee RP |
642 | /** |
643 | * sends a larger data chunk through a pipe. | |
644 | * This tests if sending and receiving data in (smaller internal) chunks works. | |
645 | */ | |
646 | void SimplePipePump() | |
647 | { | |
648 | CPPUNIT_ASSERT(backend); | |
7e586023 | 649 | |
cd9ddaee | 650 | TestPipe pipe1, pipe2; |
7e586023 | 651 | |
cd9ddaee | 652 | bool res= pipe1.makePipe(pipe2); |
7e586023 | 653 | |
cd9ddaee RP |
654 | CPPUNIT_ASSERT_EQUAL(true, res); |
655 | CPPUNIT_ASSERT_EQUAL(true, pipe1.opened()); | |
656 | CPPUNIT_ASSERT_EQUAL(true, pipe2.opened()); | |
7e586023 | 657 | |
cd9ddaee RP |
658 | res= backend->doOneStep(0); |
659 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 660 | |
cd9ddaee | 661 | std::string test_string= makeRandomAsciiString(256*1024); |
7e586023 | 662 | |
cd9ddaee | 663 | pipe1.sendString(test_string); |
7e586023 | 664 | |
cd9ddaee RP |
665 | res= backend->doOneStep(0); |
666 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 667 | |
cd9ddaee RP |
668 | // do some backend cycles to empty the pipe: |
669 | for (int i=32; i-->0 && res && !pipe1.empty(); ) | |
670 | { | |
671 | res= backend->doOneStep(100); | |
672 | }; | |
7e586023 | 673 | |
cd9ddaee RP |
674 | pipe1.close(); |
675 | CPPUNIT_ASSERT_EQUAL(false, pipe1.opened()); | |
7e586023 | 676 | |
cd9ddaee RP |
677 | // now read the remaining data until we recognize EOF: |
678 | for (int i=32; i-->0 && res && !pipe2.eof();) | |
679 | { | |
680 | res= backend->doOneStep(100); | |
681 | } | |
7e586023 | 682 | |
cd9ddaee RP |
683 | CPPUNIT_ASSERT_EQUAL( test_string.size(), pipe2.m_received_string.size() ); |
684 | CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string ); | |
7e586023 | 685 | |
cd9ddaee RP |
686 | CPPUNIT_ASSERT_EQUAL(true, pipe2.eof()); |
687 | } // eo SimplePipePump | |
7e586023 RP |
688 | |
689 | ||
690 | ||
cd9ddaee RP |
691 | /** |
692 | * fork a subprocess (/bin/true) and test exit code. | |
693 | */ | |
694 | void SimpleProcessTestBinTrue() | |
695 | { | |
696 | bool res; | |
697 | CPPUNIT_ASSERT(backend); | |
7e586023 | 698 | |
cd9ddaee | 699 | TestProcess proc("/bin/true"); |
7e586023 | 700 | |
cd9ddaee RP |
701 | res= proc.startProcess(); |
702 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 703 | |
cd9ddaee RP |
704 | res= backend->doOneStep(200); |
705 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 706 | |
cd9ddaee RP |
707 | for(int i=20; i-->0 && proc.processState() != ProcessState::stopped;) |
708 | { | |
6bf0faed | 709 | backend->doOneStep(15); |
cd9ddaee | 710 | } |
7e586023 | 711 | |
cd9ddaee RP |
712 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() ); |
713 | CPPUNIT_ASSERT_EQUAL( true, proc.eof() ); | |
714 | CPPUNIT_ASSERT_EQUAL( 0, proc.exitCode() ); | |
715 | } // eo SimpleProcessTestBinTrue | |
7e586023 RP |
716 | |
717 | ||
cd9ddaee RP |
718 | /** |
719 | * fork a subprocess (/bin/false) and test exit code. | |
720 | */ | |
721 | void SimpleProcessTestBinFalse() | |
722 | { | |
723 | bool res; | |
724 | CPPUNIT_ASSERT(backend); | |
7e586023 | 725 | |
cd9ddaee | 726 | TestProcess proc("/bin/false"); |
7e586023 | 727 | |
cd9ddaee RP |
728 | res= proc.startProcess(); |
729 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 730 | |
cd9ddaee RP |
731 | res= backend->doOneStep(200); |
732 | CPPUNIT_ASSERT_EQUAL(true, res); | |
733 | for(int i=20; i-->0 && proc.processState() != ProcessState::stopped;) | |
734 | { | |
6bf0faed | 735 | backend->doOneStep(15); |
cd9ddaee | 736 | } |
7e586023 | 737 | |
cd9ddaee RP |
738 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() ); |
739 | CPPUNIT_ASSERT_EQUAL( true, proc.eof() ); | |
740 | CPPUNIT_ASSERT_EQUAL( 1, proc.exitCode() ); | |
741 | DOUT("leave SimpleProcessTestBinFalse"); | |
742 | } // eo SimpleProcessTestBinFalse | |
7e586023 RP |
743 | |
744 | ||
cd9ddaee RP |
745 | /** |
746 | * fork an echo subprocess and read back the output. | |
747 | */ | |
748 | void SimpleProcessTestEcho() | |
749 | { | |
750 | DOUT("enter SimpleProcessTestEcho"); | |
751 | bool res; | |
752 | CPPUNIT_ASSERT(backend); | |
7e586023 | 753 | |
cd9ddaee RP |
754 | TestProcess proc( |
755 | "/bin/echo", | |
756 | TransientPushBackFiller<std::string, std::vector >()("Eine")("Zeichenkette") | |
757 | ); | |
7e586023 | 758 | |
cd9ddaee RP |
759 | res= proc.startProcess(); |
760 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 761 | |
cd9ddaee RP |
762 | res= backend->doOneStep(200); |
763 | CPPUNIT_ASSERT_EQUAL(true, res); | |
764 | for(int i=20; i-->0 && (proc.processState()!= ProcessState::stopped || !proc.eof());) | |
765 | { | |
766 | backend->doOneStep(10); | |
767 | } | |
7e586023 | 768 | |
cd9ddaee RP |
769 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() ); |
770 | CPPUNIT_ASSERT_EQUAL( true, proc.eof() ); | |
771 | CPPUNIT_ASSERT_EQUAL( 0, proc.exitCode() ); | |
772 | CPPUNIT_ASSERT_EQUAL( std::string("Eine Zeichenkette\n"), proc.m_received_string); | |
773 | } // eo SimpleProcessTestEcho | |
7e586023 RP |
774 | |
775 | ||
776 | ||
cd9ddaee RP |
777 | /** |
778 | * fork a bash subprocess, echo something on stderr and read back the output. | |
779 | */ | |
780 | void SimpleProcessTestStderr() | |
781 | { | |
782 | bool res; | |
783 | CPPUNIT_ASSERT(backend); | |
784 | ||
785 | TestIO my_stderr; | |
786 | ||
787 | TestProcess proc( | |
788 | "/bin/bash", | |
789 | TransientPushBackFiller<std::string, std::vector >() | |
790 | ("-c") | |
791 | ("echo Eine Zeichenkette >&2") | |
792 | ); | |
793 | ||
794 | // start with a seperate io object for stderr. | |
795 | DOUT("## start process"); | |
796 | res= proc.startProcess( &my_stderr ); | |
797 | CPPUNIT_ASSERT_EQUAL(true, res); | |
798 | CPPUNIT_ASSERT_EQUAL(true, my_stderr.opened()); | |
799 | ||
800 | DOUT("## do a backend step"); | |
801 | res= backend->doOneStep(200); | |
802 | CPPUNIT_ASSERT_EQUAL(true, res); | |
803 | // wait until process stopped and both io's signal EOF (or until the loop ends ;-) ) | |
804 | DOUT("## enter loop"); | |
805 | for(int i=17; i-->0 && (proc.processState()!= ProcessState::stopped || !proc.eof() || !my_stderr.eof());) | |
806 | { | |
807 | DOUT("## round i=" << i); | |
808 | backend->doOneStep(10); | |
809 | } | |
810 | DOUT("## loop left"); | |
811 | ||
812 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() ); | |
813 | CPPUNIT_ASSERT_EQUAL( true, proc.eof() ); | |
814 | CPPUNIT_ASSERT_EQUAL( true, my_stderr.eof() ); | |
815 | CPPUNIT_ASSERT_EQUAL( 0, proc.exitCode() ); | |
816 | CPPUNIT_ASSERT_EQUAL( std::string("Eine Zeichenkette\n"), my_stderr.m_received_string); | |
817 | DOUT("leave Test SimpleProcessTestStderr"); | |
818 | } // eo SimpleProcessTestStderr | |
7e586023 RP |
819 | |
820 | ||
821 | ||
cd9ddaee RP |
822 | /** |
823 | * checks termination of process by signal and if the signal is returned. | |
824 | */ | |
825 | void SignaledProcessTermination() | |
826 | { | |
827 | bool res; | |
828 | CPPUNIT_ASSERT(backend); | |
7e586023 | 829 | |
cd9ddaee RP |
830 | TestProcess proc("/bin/sleep","2"); |
831 | res= proc.startProcess(); | |
832 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 833 | |
cd9ddaee RP |
834 | res= backend->doOneStep(10); |
835 | CPPUNIT_ASSERT_EQUAL(true, res); | |
836 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::running), proc.processState() ); | |
7e586023 | 837 | |
cd9ddaee | 838 | res= backend->doOneStep(50); |
7e586023 | 839 | |
cd9ddaee RP |
840 | // now send the process an USR1 (which terminates the process) |
841 | res=proc.kill( Signal::USR1 ); | |
842 | CPPUNIT_ASSERT_EQUAL(true, res); | |
7e586023 | 843 | |
cd9ddaee RP |
844 | // give the backend a chance to process the termination event: |
845 | for(int i=30; i-->0 && proc.processState()!=ProcessState::stopped;) backend->doOneStep(10); | |
7e586023 | 846 | |
cd9ddaee RP |
847 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() ); |
848 | CPPUNIT_ASSERT_EQUAL( true, proc.eof() ); | |
849 | CPPUNIT_ASSERT_EQUAL( Signal::USR1 , proc.exitCode()>>8 ); | |
850 | } // eo SignaledProcessTermination | |
7e586023 RP |
851 | |
852 | ||
853 | ||
cd9ddaee RP |
854 | void CallOut1() |
855 | { | |
856 | Counter count; | |
7e586023 | 857 | |
cd9ddaee RP |
858 | callOut( boost::bind(&Counter::advance, &count), 1 ); |
859 | backend->doOneStep( 10 ); | |
7e586023 | 860 | |
cd9ddaee RP |
861 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
862 | backend->doOneStep( 1100 ); | |
7e586023 | 863 | |
cd9ddaee RP |
864 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); |
865 | } // eo CallOut1() | |
7e586023 RP |
866 | |
867 | ||
868 | ||
cd9ddaee RP |
869 | void CallOut2() |
870 | { | |
871 | Counter count; | |
7e586023 | 872 | |
cd9ddaee RP |
873 | callOut( boost::bind(&Counter::advance, &count), 0.5 ); |
874 | backend->doOneStep( 10 ); | |
7e586023 | 875 | |
cd9ddaee RP |
876 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
877 | backend->doOneStep( 800 ); | |
7e586023 | 878 | |
cd9ddaee RP |
879 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); |
880 | } // eo CallOut2() | |
7e586023 RP |
881 | |
882 | ||
883 | ||
cd9ddaee RP |
884 | void RemoveCallOut1() |
885 | { | |
886 | Counter count; | |
7e586023 | 887 | |
cd9ddaee RP |
888 | CallOutId id= callOut( boost::bind(&Counter::advance, &count), 1 ); |
889 | backend->doOneStep( 10 ); | |
7e586023 | 890 | |
cd9ddaee RP |
891 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
892 | bool res1 = removeCallOut(id); | |
893 | bool res2 = removeCallOut(id); | |
7e586023 | 894 | |
cd9ddaee RP |
895 | CPPUNIT_ASSERT_EQUAL( true, res1 ); |
896 | CPPUNIT_ASSERT_EQUAL( false, res2 ); | |
7e586023 | 897 | |
cd9ddaee | 898 | backend->doOneStep( 1100 ); |
7e586023 | 899 | |
cd9ddaee RP |
900 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
901 | } // eo RemoveCallOut1() | |
7e586023 RP |
902 | |
903 | ||
904 | ||
cd9ddaee RP |
905 | void FrozenCall_Thaw() |
906 | { | |
907 | Counter count; | |
7e586023 | 908 | |
cd9ddaee RP |
909 | CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 ); |
910 | backend->doOneStep( 10 ); | |
7e586023 | 911 | |
cd9ddaee RP |
912 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
913 | id.thaw(); | |
7e586023 | 914 | |
cd9ddaee | 915 | backend->doOneStep( 1100 ); |
7e586023 | 916 | |
cd9ddaee RP |
917 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); |
918 | } // eo FrozenCall_Thaw() | |
7e586023 RP |
919 | |
920 | ||
921 | ||
cd9ddaee RP |
922 | void FrozenCall_Decay() |
923 | { | |
924 | Counter count; | |
7e586023 | 925 | |
cd9ddaee RP |
926 | CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 ); |
927 | backend->doOneStep( 10 ); | |
7e586023 | 928 | |
cd9ddaee RP |
929 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
930 | CPPUNIT_ASSERT_EQUAL( true, id.active() ); | |
931 | backend->doOneStep( 1100 ); | |
7e586023 | 932 | |
cd9ddaee RP |
933 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); |
934 | CPPUNIT_ASSERT_EQUAL( false, id.active() ); | |
935 | } // eo FrozenCall_Decay() | |
7e586023 RP |
936 | |
937 | ||
938 | ||
cd9ddaee RP |
939 | void UnixSockets_ClientServer() |
940 | { | |
941 | std::string path= getCheckFilepath("UDS_CS"); | |
7e586023 | 942 | |
cd9ddaee RP |
943 | UnixIOSocketHolder server_holder; |
944 | UnixServerSocket< TestUnixIOSocket > server_port; | |
945 | UnixIOSocketPtr server; | |
946 | TestUnixIOSocket client0; | |
947 | TestUnixIOSocket client1; | |
7e586023 | 948 | |
cd9ddaee RP |
949 | bool res1 = server_port.open(path, 0600); |
950 | CPPUNIT_ASSERT_EQUAL( true, res1 ); | |
7e586023 | 951 | |
cd9ddaee | 952 | { |
0630284f | 953 | AsyncIo::Utils::FileStat stat(path,false); |
cd9ddaee RP |
954 | CPPUNIT_ASSERT( stat.is_socket() ); |
955 | CPPUNIT_ASSERT_EQUAL( 0600u, (stat.mode() & 0777)); | |
956 | } | |
7e586023 | 957 | |
cd9ddaee RP |
958 | server_port.setNewConnectionCallback( |
959 | boost::bind( &UnixIOSocketHolder::store, &server_holder, _1) | |
960 | ); | |
7e586023 | 961 | |
cd9ddaee RP |
962 | // open a first client |
963 | bool res2= client0.open(path); | |
964 | CPPUNIT_ASSERT_EQUAL( true, res2 ); | |
7e586023 | 965 | |
cd9ddaee RP |
966 | CPPUNIT_ASSERT_EQUAL(0u, server_holder.size() ); |
967 | backendStep(5,1); | |
968 | CPPUNIT_ASSERT_EQUAL(1u, server_holder.size() ); | |
969 | CPPUNIT_ASSERT( server_holder.get(0).get() ); | |
7e586023 | 970 | |
cd9ddaee RP |
971 | client0.sendData("a simple test string."); |
972 | backendStep(3,2); | |
7e586023 | 973 | |
cd9ddaee RP |
974 | CPPUNIT_ASSERT_EQUAL( |
975 | std::string("a simple test string."), | |
976 | server_holder.get(0)->m_received_string | |
977 | ); | |
978 | server_holder.get(0)->sendData("reply 1"); | |
979 | backendStep(3,2); | |
980 | CPPUNIT_ASSERT_EQUAL( std::string("reply 1"), client0.m_received_string ); | |
7e586023 | 981 | |
cd9ddaee RP |
982 | // open a second client |
983 | res2= client1.open(path); | |
984 | CPPUNIT_ASSERT_EQUAL( true, res2 ); | |
985 | backendStep(5,1); | |
986 | CPPUNIT_ASSERT_EQUAL(2u, server_holder.size() ); | |
987 | CPPUNIT_ASSERT( server_holder.get(1).get() ); | |
7e586023 | 988 | |
cd9ddaee RP |
989 | server_holder.get(1)->sendData("::reply 2"); |
990 | backendStep(3,2); | |
991 | CPPUNIT_ASSERT_EQUAL( std::string("::reply 2"), client1.m_received_string ); | |
7e586023 | 992 | |
cd9ddaee RP |
993 | client1.sendData("another simple test string. 124"); |
994 | backendStep(3,2); | |
7e586023 | 995 | |
cd9ddaee RP |
996 | CPPUNIT_ASSERT_EQUAL( |
997 | std::string("another simple test string. 124"), | |
998 | server_holder.get(1)->m_received_string | |
999 | ); | |
7e586023 | 1000 | |
cd9ddaee RP |
1001 | // close first client |
1002 | client0.close(); | |
1003 | CPPUNIT_ASSERT_EQUAL( false, server_holder.get(0)->eof() ); | |
1004 | backendStep(3,2); | |
1005 | CPPUNIT_ASSERT_EQUAL( true, server_holder.get(0)->eof() ); | |
1006 | server_holder.get(0)->close(); | |
7e586023 | 1007 | |
cd9ddaee RP |
1008 | // close second connection from server side |
1009 | CPPUNIT_ASSERT_EQUAL( false, client1.eof() ); | |
1010 | server_holder.get(1)->close(); | |
1011 | backendStep(3,2); | |
1012 | CPPUNIT_ASSERT_EQUAL( true, client1.eof() ); | |
1013 | client1.close(); | |
1014 | } // eo UnixSockets_ClientServer() | |
7e586023 RP |
1015 | |
1016 | ||
cd9ddaee RP |
1017 | void Dummy() |
1018 | { | |
1019 | using namespace std; | |
1020 | cout << endl << "Random strings:" << endl; | |
1021 | for (int i=10; i-->0;) | |
1022 | { | |
1023 | cout << " " << makeRandomAsciiString(70)<< endl; | |
1024 | } | |
1025 | } // eo Dummy | |
7e586023 RP |
1026 | |
1027 | ||
cd9ddaee RP |
1028 | }; // eo class TestSimpleioBasics |
1029 | ||
1030 | ||
1031 | CPPUNIT_TEST_SUITE_REGISTRATION(TestSimpleioBasics); |