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 | * | |
22 | * (c) Copyright 2007 by Intra2net AG | |
cd9ddaee RP |
23 | */ |
24 | ||
25 | //#define NOISEDEBUG | |
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> | |
0630284f TJ |
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; | |
68 | ||
69 | ||
70 | Counter() : value(0) { DOUT("Counter construct");} | |
71 | ||
72 | void reset() { value=0;} | |
73 | ||
74 | void advance() | |
75 | { | |
76 | DOUT(" advance called"); | |
77 | ++value; | |
78 | } | |
79 | ||
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: | |
91 | ||
92 | TestTimer() | |
93 | : m_counter(0u) | |
94 | { | |
95 | } // eo TestTimer() | |
96 | ||
97 | ||
98 | void setDelta(long msec) | |
99 | { | |
100 | setDeltaWhenTime(0,msec); | |
101 | activate(); | |
102 | } // eo setDelta(long) | |
103 | ||
104 | ||
105 | unsigned m_counter; | |
106 | ||
107 | protected: | |
108 | ||
109 | virtual void execute() | |
110 | { | |
111 | ++m_counter; | |
112 | } // eo execute() | |
113 | ||
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; | |
122 | ||
123 | unsigned long m_count_lines; | |
124 | unsigned long m_data_size; | |
125 | ||
126 | ||
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&) | |
143 | ||
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 | ||
158 | ||
159 | protected: | |
160 | ||
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>&) | |
185 | ||
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>&) | |
196 | ||
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: | |
215 | ||
216 | void slotReceivedData() | |
217 | { | |
218 | receiveData(m_input_buffer); | |
219 | m_input_buffer.clear(); | |
220 | } // eo slotReceivedData() | |
221 | ||
222 | }; // eo class TestProcess | |
223 | ||
224 | ||
225 | ||
226 | class TestUnixIOSocket | |
227 | : public UnixIOSocket | |
228 | , public ReceivedData | |
229 | { | |
230 | public: | |
231 | ||
232 | TestUnixIOSocket() | |
233 | : UnixIOSocket() | |
234 | { | |
235 | m_signal_read.connect( boost::bind(&TestUnixIOSocket::slotReceivedData, this) ); | |
236 | } // eo TestUnixIOSocket() | |
237 | ||
238 | ||
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() | |
247 | ||
248 | ||
249 | void sendData(const std::string& data) | |
250 | { | |
251 | lowSend(data); | |
252 | } // eo sendData(const std::string&) | |
253 | ||
254 | protected: | |
255 | ||
256 | void slotReceivedData() | |
257 | { | |
258 | receiveData(m_input_buffer); | |
259 | m_input_buffer.clear(); | |
260 | } // eo slotReceivedData() | |
261 | ||
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: | |
271 | ||
272 | void operator()(UnixIOSocketPtr ptr) | |
273 | { | |
274 | push_back(ptr); | |
275 | } | |
276 | ||
277 | void storeBase (IOImplementationPtr ptr) | |
278 | { | |
279 | push_back(boost::dynamic_pointer_cast< UnixIOSocket >(ptr) ); | |
280 | } | |
281 | ||
282 | void store (UnixIOSocketPtr ptr) | |
283 | { | |
284 | push_back(ptr); | |
285 | } | |
286 | ||
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; | |
309 | ||
310 | boost::uniform_int<> discreter(0, chars.size()-1); | |
311 | boost::variate_generator<boost::mt19937&, boost::uniform_int<> > idxgen(g_random_gen, discreter); | |
312 | ||
313 | for(; len-->0;) s+= chars.at( idxgen() ); | |
314 | ||
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); | |
326 | ||
327 | CPPUNIT_TEST(EmptyBackendStepCall); | |
328 | CPPUNIT_TEST(NonEmptyBackendStepCall); | |
329 | CPPUNIT_TEST(SingleTimerShot); | |
330 | CPPUNIT_TEST(SimpleTimerShot); | |
331 | CPPUNIT_TEST(SimpleTimerShot2); | |
332 | ||
333 | CPPUNIT_TEST(EmptyWantTest); | |
334 | CPPUNIT_TEST(SimplePipeTest); | |
335 | CPPUNIT_TEST(SimplePipePump); | |
336 | ||
337 | CPPUNIT_TEST(SimpleProcessTestBinTrue); | |
338 | CPPUNIT_TEST(SimpleProcessTestBinFalse); | |
339 | CPPUNIT_TEST(SimpleProcessTestEcho); | |
340 | CPPUNIT_TEST(SimpleProcessTestStderr); | |
341 | CPPUNIT_TEST(SignaledProcessTermination); | |
342 | ||
343 | ||
344 | CPPUNIT_TEST(CallOut1); | |
345 | CPPUNIT_TEST(CallOut2); | |
346 | CPPUNIT_TEST(RemoveCallOut1); | |
347 | ||
348 | CPPUNIT_TEST(FrozenCall_Thaw); | |
349 | CPPUNIT_TEST(FrozenCall_Decay); | |
350 | ||
351 | ||
352 | CPPUNIT_TEST(UnixSockets_ClientServer); | |
353 | ||
354 | ||
355 | //CPPUNIT_TEST(Dummy); | |
356 | CPPUNIT_TEST_SUITE_END(); | |
357 | ||
358 | protected: | |
359 | ||
360 | Backend *backend; | |
361 | std::set<std::string> used_check_files; | |
362 | ||
363 | ||
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 | |
373 | ||
374 | ||
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 | |
384 | ||
385 | ||
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 | |
393 | ||
394 | ||
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); | |
0630284f | 402 | //FIXME: if (path_exists(filepath)) |
cd9ddaee | 403 | { |
0630284f | 404 | unlink(filepath.c_str()); |
cd9ddaee RP |
405 | } |
406 | } | |
407 | used_check_files.clear(); | |
408 | } // eo removeCheckFiles | |
409 | ||
410 | ||
411 | ||
412 | public: | |
413 | ||
414 | void setUp() | |
415 | { | |
416 | backend = Backend::getBackend(); | |
417 | installChildHandler(); | |
418 | used_check_files.clear(); | |
419 | } // eo setUp | |
420 | ||
421 | ||
422 | void tearDown() | |
423 | { | |
424 | restoreChildHandler(); | |
425 | removeCheckFiles(); | |
426 | } // eo tearDown | |
427 | ||
428 | ||
429 | /* | |
430 | * the tests: | |
431 | */ | |
432 | ||
433 | ||
434 | /* | |
435 | ** basics: | |
436 | */ | |
437 | ||
438 | ||
439 | void EmptyBackendStepCall() | |
440 | { | |
441 | CPPUNIT_ASSERT( backend ); | |
442 | ||
443 | // a backend call without active objects should return false: | |
444 | bool result = backend->doOneStep(0); | |
445 | ||
446 | CPPUNIT_ASSERT_EQUAL( false, result ); | |
447 | } // eo EmptyBackendStepCall | |
448 | ||
449 | ||
450 | ||
451 | void NonEmptyBackendStepCall() | |
452 | { | |
453 | CPPUNIT_ASSERT(backend); | |
454 | ||
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 | |
468 | ||
469 | ||
470 | ||
471 | /** | |
472 | * check for timer to execute immediatly. | |
473 | */ | |
474 | void SingleTimerShot() | |
475 | { | |
476 | CPPUNIT_ASSERT(backend); | |
477 | ||
478 | TestTimer timer; | |
479 | timer.setDelta(0); // shot now! | |
480 | ||
481 | bool result = backend->doOneStep(10); | |
482 | ||
483 | CPPUNIT_ASSERT_EQUAL( true, result ); | |
484 | // the timer should be executed once: | |
485 | CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter ); | |
486 | ||
487 | result = backend->doOneStep(0); | |
488 | ||
489 | CPPUNIT_ASSERT_EQUAL( false, result ); | |
490 | // the timer should not be executed again: | |
491 | CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter ); | |
492 | ||
493 | } // eo SingleTimerShot() | |
494 | ||
495 | ||
496 | ||
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); | |
504 | ||
505 | SimpleTimer timer1; | |
506 | Counter counter1; | |
507 | timer1.addAction( boost::bind(&Counter::advance,&counter1) ); | |
508 | CPPUNIT_ASSERT_EQUAL(false, timer1.active()); | |
509 | ||
510 | timer1.startTimerMS( 100 ); | |
511 | CPPUNIT_ASSERT_EQUAL(true, timer1.active()); | |
512 | ||
513 | res=backend->doOneStep( 1000 ); | |
514 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
515 | ||
516 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); | |
517 | } // eo SimpleTimerShot | |
518 | ||
519 | ||
520 | ||
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); | |
528 | ||
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()); | |
537 | ||
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()); | |
544 | ||
545 | res=backend->doOneStep( 1000 ); | |
546 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
547 | ||
548 | CPPUNIT_ASSERT_EQUAL(false, timer1.active()); | |
549 | CPPUNIT_ASSERT_EQUAL(true, timer2.active()); | |
550 | CPPUNIT_ASSERT_EQUAL(true, timer3.active()); | |
551 | ||
552 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); | |
553 | CPPUNIT_ASSERT_EQUAL( 0, counter2.value ); | |
554 | CPPUNIT_ASSERT_EQUAL( 0, counter3.value ); | |
555 | ||
556 | // now stop the next timer: | |
557 | timer3.stopTimer(); | |
558 | CPPUNIT_ASSERT_EQUAL(false, timer3.active()); | |
559 | ||
560 | res=backend->doOneStep( 1000 ); | |
561 | CPPUNIT_ASSERT_EQUAL( true, res ); | |
562 | ||
563 | CPPUNIT_ASSERT_EQUAL( 1, counter1.value ); | |
564 | CPPUNIT_ASSERT_EQUAL( 1, counter2.value ); | |
565 | CPPUNIT_ASSERT_EQUAL( 0, counter3.value ); | |
566 | } // eo SimpleTimerShot2 | |
567 | ||
568 | ||
569 | ||
570 | ||
571 | /* | |
572 | ** I/O tests: | |
573 | */ | |
574 | ||
575 | void EmptyWantTest() | |
576 | { | |
577 | IOImplementation io; | |
578 | ||
579 | CPPUNIT_ASSERT_EQUAL(false, io.wantRead() ); | |
580 | CPPUNIT_ASSERT_EQUAL(false, io.wantWrite() ); | |
581 | } // eo EmptyWantTest | |
582 | ||
583 | ||
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 | * . | |
597 | * | |
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"); | |
603 | ||
604 | CPPUNIT_ASSERT(backend); | |
605 | ||
606 | TestPipe pipe1, pipe2; | |
607 | ||
608 | bool res= pipe1.makePipe(pipe2); | |
609 | ||
610 | CPPUNIT_ASSERT_EQUAL(true, res); | |
611 | CPPUNIT_ASSERT_EQUAL(true, pipe1.opened()); | |
612 | CPPUNIT_ASSERT_EQUAL(true, pipe2.opened()); | |
613 | ||
614 | res= backend->doOneStep(0); | |
615 | CPPUNIT_ASSERT_EQUAL(true, res); | |
616 | ||
617 | pipe1.sendString(test_string); | |
618 | ||
619 | res= backend->doOneStep(0); | |
620 | CPPUNIT_ASSERT_EQUAL(true, res); | |
621 | ||
622 | CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string ); | |
623 | ||
624 | pipe2.sendString(test_string2); | |
625 | ||
626 | res= backend->doOneStep(0); | |
627 | CPPUNIT_ASSERT_EQUAL(true, res); | |
628 | ||
629 | CPPUNIT_ASSERT_EQUAL( test_string2, pipe1.m_received_string ); | |
630 | ||
631 | pipe1.close(); | |
632 | CPPUNIT_ASSERT_EQUAL(false, pipe1.opened()); | |
633 | ||
634 | res= backend->doOneStep(0); | |
635 | CPPUNIT_ASSERT_EQUAL(true, res); | |
636 | ||
637 | CPPUNIT_ASSERT_EQUAL(true, pipe2.eof()); | |
638 | } // eo SimplePipeTest | |
639 | ||
640 | ||
641 | ||
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); | |
649 | ||
650 | TestPipe pipe1, pipe2; | |
651 | ||
652 | bool res= pipe1.makePipe(pipe2); | |
653 | ||
654 | CPPUNIT_ASSERT_EQUAL(true, res); | |
655 | CPPUNIT_ASSERT_EQUAL(true, pipe1.opened()); | |
656 | CPPUNIT_ASSERT_EQUAL(true, pipe2.opened()); | |
657 | ||
658 | res= backend->doOneStep(0); | |
659 | CPPUNIT_ASSERT_EQUAL(true, res); | |
660 | ||
661 | std::string test_string= makeRandomAsciiString(256*1024); | |
662 | ||
663 | pipe1.sendString(test_string); | |
664 | ||
665 | res= backend->doOneStep(0); | |
666 | CPPUNIT_ASSERT_EQUAL(true, res); | |
667 | ||
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 | }; | |
673 | ||
674 | pipe1.close(); | |
675 | CPPUNIT_ASSERT_EQUAL(false, pipe1.opened()); | |
676 | ||
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 | } | |
682 | ||
683 | CPPUNIT_ASSERT_EQUAL( test_string.size(), pipe2.m_received_string.size() ); | |
684 | CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string ); | |
685 | ||
686 | CPPUNIT_ASSERT_EQUAL(true, pipe2.eof()); | |
687 | } // eo SimplePipePump | |
688 | ||
689 | ||
690 | ||
691 | /** | |
692 | * fork a subprocess (/bin/true) and test exit code. | |
693 | */ | |
694 | void SimpleProcessTestBinTrue() | |
695 | { | |
696 | bool res; | |
697 | CPPUNIT_ASSERT(backend); | |
698 | ||
699 | TestProcess proc("/bin/true"); | |
700 | ||
701 | res= proc.startProcess(); | |
702 | CPPUNIT_ASSERT_EQUAL(true, res); | |
703 | ||
704 | res= backend->doOneStep(200); | |
705 | CPPUNIT_ASSERT_EQUAL(true, res); | |
706 | ||
707 | for(int i=20; i-->0 && proc.processState() != ProcessState::stopped;) | |
708 | { | |
6bf0faed | 709 | backend->doOneStep(15); |
cd9ddaee RP |
710 | } |
711 | ||
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 | |
716 | ||
717 | ||
718 | /** | |
719 | * fork a subprocess (/bin/false) and test exit code. | |
720 | */ | |
721 | void SimpleProcessTestBinFalse() | |
722 | { | |
723 | bool res; | |
724 | CPPUNIT_ASSERT(backend); | |
725 | ||
726 | TestProcess proc("/bin/false"); | |
727 | ||
728 | res= proc.startProcess(); | |
729 | CPPUNIT_ASSERT_EQUAL(true, res); | |
730 | ||
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 RP |
736 | } |
737 | ||
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 | |
743 | ||
744 | ||
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); | |
753 | ||
754 | TestProcess proc( | |
755 | "/bin/echo", | |
756 | TransientPushBackFiller<std::string, std::vector >()("Eine")("Zeichenkette") | |
757 | ); | |
758 | ||
759 | res= proc.startProcess(); | |
760 | CPPUNIT_ASSERT_EQUAL(true, res); | |
761 | ||
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 | } | |
768 | ||
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 | |
774 | ||
775 | ||
776 | ||
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 | |
819 | ||
820 | ||
821 | ||
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); | |
829 | ||
830 | TestProcess proc("/bin/sleep","2"); | |
831 | res= proc.startProcess(); | |
832 | CPPUNIT_ASSERT_EQUAL(true, res); | |
833 | ||
834 | res= backend->doOneStep(10); | |
835 | CPPUNIT_ASSERT_EQUAL(true, res); | |
836 | CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::running), proc.processState() ); | |
837 | ||
838 | res= backend->doOneStep(50); | |
839 | ||
840 | // now send the process an USR1 (which terminates the process) | |
841 | res=proc.kill( Signal::USR1 ); | |
842 | CPPUNIT_ASSERT_EQUAL(true, res); | |
843 | ||
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); | |
846 | ||
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 | |
851 | ||
852 | ||
853 | ||
854 | void CallOut1() | |
855 | { | |
856 | Counter count; | |
857 | ||
858 | callOut( boost::bind(&Counter::advance, &count), 1 ); | |
859 | backend->doOneStep( 10 ); | |
860 | ||
861 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
862 | backend->doOneStep( 1100 ); | |
863 | ||
864 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); | |
865 | } // eo CallOut1() | |
866 | ||
867 | ||
868 | ||
869 | void CallOut2() | |
870 | { | |
871 | Counter count; | |
872 | ||
873 | callOut( boost::bind(&Counter::advance, &count), 0.5 ); | |
874 | backend->doOneStep( 10 ); | |
875 | ||
876 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
877 | backend->doOneStep( 800 ); | |
878 | ||
879 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); | |
880 | } // eo CallOut2() | |
881 | ||
882 | ||
883 | ||
884 | void RemoveCallOut1() | |
885 | { | |
886 | Counter count; | |
887 | ||
888 | CallOutId id= callOut( boost::bind(&Counter::advance, &count), 1 ); | |
889 | backend->doOneStep( 10 ); | |
890 | ||
891 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
892 | bool res1 = removeCallOut(id); | |
893 | bool res2 = removeCallOut(id); | |
894 | ||
895 | CPPUNIT_ASSERT_EQUAL( true, res1 ); | |
896 | CPPUNIT_ASSERT_EQUAL( false, res2 ); | |
897 | ||
898 | backend->doOneStep( 1100 ); | |
899 | ||
900 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
901 | } // eo RemoveCallOut1() | |
902 | ||
903 | ||
904 | ||
905 | void FrozenCall_Thaw() | |
906 | { | |
907 | Counter count; | |
908 | ||
909 | CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 ); | |
910 | backend->doOneStep( 10 ); | |
911 | ||
912 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
913 | id.thaw(); | |
914 | ||
915 | backend->doOneStep( 1100 ); | |
916 | ||
917 | CPPUNIT_ASSERT_EQUAL( 1, count.value ); | |
918 | } // eo FrozenCall_Thaw() | |
919 | ||
920 | ||
921 | ||
922 | void FrozenCall_Decay() | |
923 | { | |
924 | Counter count; | |
925 | ||
926 | CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 ); | |
927 | backend->doOneStep( 10 ); | |
928 | ||
929 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
930 | CPPUNIT_ASSERT_EQUAL( true, id.active() ); | |
931 | backend->doOneStep( 1100 ); | |
932 | ||
933 | CPPUNIT_ASSERT_EQUAL( 0, count.value ); | |
934 | CPPUNIT_ASSERT_EQUAL( false, id.active() ); | |
935 | } // eo FrozenCall_Decay() | |
936 | ||
937 | ||
938 | ||
939 | void UnixSockets_ClientServer() | |
940 | { | |
941 | std::string path= getCheckFilepath("UDS_CS"); | |
942 | ||
943 | UnixIOSocketHolder server_holder; | |
944 | UnixServerSocket< TestUnixIOSocket > server_port; | |
945 | UnixIOSocketPtr server; | |
946 | TestUnixIOSocket client0; | |
947 | TestUnixIOSocket client1; | |
948 | ||
949 | bool res1 = server_port.open(path, 0600); | |
950 | CPPUNIT_ASSERT_EQUAL( true, res1 ); | |
951 | ||
952 | { | |
0630284f | 953 | AsyncIo::Utils::FileStat stat(path,false); |
cd9ddaee | 954 | CPPUNIT_ASSERT( stat.is_socket() ); |
0630284f | 955 | //FIXME: CPPUNIT_ASSERT_EQUAL( 0600u, (stat.mode() & 0777)); |
cd9ddaee RP |
956 | } |
957 | ||
958 | server_port.setNewConnectionCallback( | |
959 | boost::bind( &UnixIOSocketHolder::store, &server_holder, _1) | |
960 | ); | |
961 | ||
962 | // open a first client | |
963 | bool res2= client0.open(path); | |
964 | CPPUNIT_ASSERT_EQUAL( true, res2 ); | |
965 | ||
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() ); | |
970 | ||
971 | client0.sendData("a simple test string."); | |
972 | backendStep(3,2); | |
973 | ||
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 ); | |
981 | ||
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() ); | |
988 | ||
989 | server_holder.get(1)->sendData("::reply 2"); | |
990 | backendStep(3,2); | |
991 | CPPUNIT_ASSERT_EQUAL( std::string("::reply 2"), client1.m_received_string ); | |
992 | ||
993 | client1.sendData("another simple test string. 124"); | |
994 | backendStep(3,2); | |
995 | ||
996 | CPPUNIT_ASSERT_EQUAL( | |
997 | std::string("another simple test string. 124"), | |
998 | server_holder.get(1)->m_received_string | |
999 | ); | |
1000 | ||
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(); | |
1007 | ||
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() | |
1015 | ||
1016 | ||
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 | |
1026 | ||
1027 | ||
1028 | }; // eo class TestSimpleioBasics | |
1029 | ||
1030 | ||
1031 | CPPUNIT_TEST_SUITE_REGISTRATION(TestSimpleioBasics); |