switch to new MPL based Fraunhofer FOKUS Public License
[senf.git] / senf / Scheduler / FdEvent.test.cc
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief FdEvent unit tests */
30
31 //#include "FdEvent.test.hh"
32 //#include "FdEvent.test.ih"
33
34 // Custom includes
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #include <sys/socket.h>
41 #include <sys/un.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <iostream>
45
46 #include "FdEvent.hh"
47
48 #include <boost/bind.hpp>
49
50 #include <senf/Utils/auto_unit_test.hh>
51 #include <boost/test/test_tools.hpp>
52
53 #define prefix_
54 //-/////////////////////////////////////////////////////////////////////////////////////////////////
55
56 namespace {
57
58     char const * SOCK_PATH = "/tmp/sched_test.sock";
59
60     void error(char const * fn, char const * proc="")
61     {
62         std::cerr << "\n" << proc << fn << ": " << strerror(errno) << std::endl;
63     }
64
65     void fail(char const * fn)
66     {
67         error(fn,"server: ");
68         _exit(1);
69     }
70
71     void server()
72     {
73         int sock = socket(PF_UNIX,SOCK_STREAM,0);
74         if (sock<0) fail("socket");
75         struct sockaddr_un sun;
76         memset(&sun,0,sizeof(sun));
77         sun.sun_family = AF_UNIX;
78         strcpy(sun.sun_path,SOCK_PATH);
79         if (bind(sock,(struct sockaddr*)&sun,sizeof(sun))<0) fail("bind");
80         if (listen(sock,1)<0) fail("listen");
81         int conn = accept(sock,0,0);
82         if (conn < 0) fail("accept");
83
84         //-/////////////////////////////////////////////////////////////////////////////////////////
85
86         if (write(conn,"READ",4)<0) fail("write");
87         char buffer[1024];
88         int size =  read(conn,buffer,1024);
89         if (size<0) fail("read");
90         if (size == 5) {
91             buffer[5] = 0;
92             if (strcmp(buffer,"WRITE")==0) {
93                 if (write(conn,"OK",2)<0) fail("write");
94             } else
95                 if (write(conn,"FAIL",4)<0) fail("write");
96         } else
97             if (write(conn,"FAIL",4)<0) fail("write");
98
99         //-/////////////////////////////////////////////////////////////////////////////////////////
100
101         close(conn);
102         close(sock);
103     }
104
105     int start_server()
106     {
107         unlink(SOCK_PATH);
108         int pid = fork();
109         if (pid == 0) {
110             signal(SIGCHLD, SIG_IGN);
111             server();
112             _exit(0);
113         }
114         if (pid < 0) {
115             error("fork");
116             return 0;
117         }
118         signal(SIGCHLD, SIG_DFL);
119
120         sleep(1); // Wait for the server socket to be opened
121         return pid;
122     }
123
124     bool stop_server(int pid)
125     {
126         sleep(1); // Wait for the server to terminate
127         if (kill(pid,SIGTERM)<0) {
128             error("kill");
129             return false;
130         }
131         int status = 0;
132         if (waitpid(pid,&status,0)<0) {
133             error("waitpid");
134             return false;
135         }
136         unlink(SOCK_PATH);
137         if (WIFSIGNALED(status)) {
138             std::cerr << "\nserver terminated with signal " << WTERMSIG(status) << std::endl;
139             return false;
140         }
141         if (WEXITSTATUS(status)!=0) {
142             std::cerr << "\nserver terminated with exit status " << WEXITSTATUS(status) << std::endl;
143             return false;
144         }
145         return true;
146     }
147
148     char buffer[1024];
149     int size (0);
150     int event (0);
151     int calls (0);
152
153     void callback(int fd, int ev)
154     {
155         ++calls;
156         event = ev;
157         switch (event & senf::scheduler::FdEvent::EV_ALL) {
158         case senf::scheduler::FdEvent::EV_READ:
159             size = recv(fd,buffer,1024,0);
160             break;
161         case senf::scheduler::FdEvent::EV_PRIO:
162             size = recv(fd,buffer,1024,MSG_OOB);
163             break;
164         case senf::scheduler::FdEvent::EV_WRITE:
165             size = write(fd,buffer,size);
166             break;
167         }
168     }
169
170 }
171
172 SENF_AUTO_UNIT_TEST(fdDispatcher)
173 {
174     senf::scheduler::detail::FdManager::instance().timeout(1000);
175
176     int pid (start_server());
177     BOOST_REQUIRE( pid );
178
179     int sock = socket(PF_UNIX,SOCK_STREAM,0);
180     if (sock<0) {
181         error("socket");
182         BOOST_FAIL("socket");
183     }
184     struct sockaddr_un sun;
185     memset(&sun,0,sizeof(sun));
186     sun.sun_family = AF_UNIX;
187     strcpy(sun.sun_path,SOCK_PATH);
188
189     if (connect(sock,(struct sockaddr*)&sun,sizeof(sun))<0) {
190         error("connect");
191         BOOST_FAIL("connect");
192     }
193
194     {
195         senf::scheduler::FdEvent sockread ("testHandler",  boost::bind(&callback, sock, _1),
196                                            sock, senf::scheduler::FdEvent::EV_READ);
197         senf::scheduler::FdEvent sockwrite ("testHandler", boost::bind(&callback, sock, _1),
198                                             sock, senf::scheduler::FdEvent::EV_WRITE, false);
199         event = 0;
200         SENF_CHECK_NO_THROW( senf::scheduler::detail::FdManager::instance().processOnce() );
201         SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
202         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_READ );
203         BOOST_CHECK_EQUAL( size, 4 );
204         buffer[size] = 0;
205         BOOST_CHECK_EQUAL( buffer, "READ" );
206
207         strcpy(buffer,"WRITE");
208         size=5;
209         SENF_CHECK_NO_THROW( sockwrite.enable() );
210         event = 0;
211         sleep(1);
212         SENF_CHECK_NO_THROW( senf::scheduler::detail::FdManager::instance().processOnce() );
213         SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
214         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_WRITE );
215
216         SENF_CHECK_NO_THROW( sockwrite.disable() );
217         event = 0;
218         sleep(1);
219         SENF_CHECK_NO_THROW( senf::scheduler::detail::FdManager::instance().processOnce() );
220         SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
221         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_HUP | senf::scheduler::FdEvent::EV_READ );
222         BOOST_CHECK_EQUAL( size, 2 );
223         buffer[size]=0;
224         BOOST_CHECK_EQUAL( buffer, "OK" );
225
226         BOOST_CHECK_EQUAL( calls, 3 );
227         SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
228         BOOST_CHECK_EQUAL( calls, 3 );
229
230         // Ensure, removing an already closed file-descriptor doesn't wreak havoc
231         close(sock);
232     }
233
234     SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
235     BOOST_CHECK_EQUAL( calls, 3 );
236
237     BOOST_CHECK (stop_server(pid));
238 }
239
240 namespace {
241
242     bool is_close(senf::ClockService::clock_type a, senf::ClockService::clock_type b)
243     {
244         return (a<b ? b-a : a-b) < senf::ClockService::milliseconds(50);
245     }
246
247     bool called (false);
248     void handler(int events)
249     {
250         called=true;
251     }
252 }
253
254 SENF_AUTO_UNIT_TEST(fileDispatcher)
255 {
256     char const * enabled (getenv("SENF_TIMING_CRITICAL_TESTS"));
257     BOOST_WARN_MESSAGE(enabled, "Set SENF_TIMING_CRITICAL_TESTS to not skip timing critical tests");
258
259     senf::scheduler::detail::FileDispatcher::instance().timeout(500);
260
261     int fd (open("test.empty.file", O_RDWR|O_CREAT|O_TRUNC, 0600));
262
263     senf::ClockService::clock_type t (senf::ClockService::now());
264     try {
265         senf::scheduler::FdEvent fde ("testHandler", &handler,
266                                       fd, senf::scheduler::FdEvent::EV_READ);
267         SENF_CHECK_NO_THROW( senf::scheduler::detail::FdManager::instance().processOnce() );
268         SENF_CHECK_NO_THROW( senf::scheduler::detail::FileDispatcher::instance().prepareRun() );
269         SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
270
271         BOOST_CHECK( called );
272         if (enabled)
273             BOOST_CHECK_PREDICATE( is_close, (t)(senf::ClockService::now()) );
274     }
275     catch (std::exception const & ex) {
276         std::cerr << "Exception:\n" << ex.what() << "\n";
277         throw;
278     }
279     close(fd);
280
281     called = false;
282     t = senf::ClockService::now();
283     SENF_CHECK_NO_THROW( senf::scheduler::detail::FdManager::instance().processOnce() );
284     SENF_CHECK_NO_THROW( senf::scheduler::detail::FileDispatcher::instance().prepareRun() );
285     SENF_CHECK_NO_THROW( senf::scheduler::detail::FIFORunner::instance().run() );
286
287     BOOST_CHECK( ! called );
288     if (enabled)
289         BOOST_CHECK_PREDICATE(
290             is_close, (t+senf::ClockService::milliseconds(500))(senf::ClockService::now()) );
291
292     unlink("test.empty.file");
293     senf::scheduler::detail::FileDispatcher::instance().timeout(-1);
294 }
295
296 //-/////////////////////////////////////////////////////////////////////////////////////////////////
297 #undef prefix_
298
299 \f
300 // Local Variables:
301 // mode: c++
302 // fill-column: 100
303 // comment-column: 40
304 // c-file-style: "senf"
305 // indent-tabs-mode: nil
306 // ispell-local-dictionary: "american"
307 // compile-command: "scons -u test"
308 // End: