Add new file-variable 'comment-column'
[senf.git] / Socket / TCPSocketHandle.test.cc
1 // $Id$
2 //
3 // Copyright (C) 2006
4 // Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
5 // Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
6 //     Stefan Bund <stefan.bund@fokus.fraunhofer.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 // Unit tests
24
25 //#include "TCPSocketHandle.test.hh"
26 //#include "TCPSocketHandle.test.ih"
27
28 // Custom includes
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include "TCPSocketHandle.hh"
36 #include <iostream>
37
38 #include <boost/test/auto_unit_test.hpp>
39 #include <boost/test/test_tools.hpp>
40
41 #define prefix_
42 ///////////////////////////////cc.p////////////////////////////////////////
43
44 namespace {
45
46     void error(char const * fn, char const * proc="")
47     {
48         std::cerr << "\n" << proc << ((*proc)?": ":"") << fn << ": " << strerror(errno) << std::endl;
49     }
50
51     void fail(char const * proc, char const * fn)
52     {
53         error(fn,proc);
54         _exit(1);
55     }
56
57     int server_pid = 0;
58
59     void start(void (*fn)())
60     {
61         server_pid = ::fork();
62         if (server_pid < 0) BOOST_FAIL("fork()");
63         if (server_pid == 0) {
64             (*fn)();
65             _exit(0);
66         }
67     }
68
69     void wait()
70     {
71         int status;
72         if (waitpid(server_pid,&status,0)<0)
73             BOOST_FAIL("waitpid()");
74         BOOST_CHECK_EQUAL( status , 0 );
75     }
76
77     void stop()
78     {
79         if (server_pid) {
80             kill(server_pid,9);
81             wait();
82             server_pid = 0;
83         }
84     }
85
86 }
87
88 ///////////////////////////////////////////////////////////////////////////
89
90 namespace {
91
92     void server_v4()
93     {
94         int serv = socket(PF_INET,SOCK_STREAM,0);
95         if (serv<0) fail("server_v4","socket()");
96         int v = 1;
97         if (setsockopt(serv,SOL_SOCKET,SO_REUSEADDR,&v,sizeof(v))<0)
98             fail("server_v4","setsockopt()");
99         struct sockaddr_in sin;
100         ::memset(&sin,0,sizeof(sin));
101         sin.sin_family = AF_INET;
102         sin.sin_port = htons(12345);
103         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
104         if (bind(serv,(struct sockaddr *)&sin,sizeof(sin))<0) fail("server_v4","bind()");
105         if (listen(serv,1)<0) fail("server_v4","listen()");
106         int sock = accept(serv,0,0);
107         if (sock < 0) fail("server_v4","accept()");
108
109         char buffer[1024];
110         while (1) {
111             int n = read(sock,buffer,1024);
112             if (n == 4 && strncmp(buffer,"QUIT",4) == 0)
113                 break;
114             write(sock,buffer,n);
115         }
116
117         if (shutdown(sock, SHUT_RDWR) < 0) fail("server_v4","shutdown()");
118         if (close(sock) < 0) fail("server_v4","close()");
119         if (close(serv) < 0) fail("server_v4","close()");
120     }
121
122     void server_v6()
123     {
124         int serv = socket(PF_INET6,SOCK_STREAM,0);
125         if (serv<0) fail("server_v6","socket()");
126         int v = 1;
127         if (setsockopt(serv,SOL_SOCKET,SO_REUSEADDR,&v,sizeof(v))<0)
128             fail("server_v6","setsockopt()");
129         struct sockaddr_in6 sin;
130         ::memset(&sin,0,sizeof(sin));
131         sin.sin6_family = AF_INET6;
132         sin.sin6_port = htons(12345);
133         sin.sin6_addr = in6addr_loopback;
134         if (bind(serv,(struct sockaddr *)&sin,sizeof(sin))<0) fail("server_v6","bind()");
135         if (listen(serv,1)<0) fail("server_v6","listen()");
136         int sock = accept(serv,0,0);
137         if (sock < 0) fail("server_v6","accept()");
138
139         char buffer[1024];
140         while (1) {
141             int n = read(sock,buffer,1024);
142             if (n == 4 && strncmp(buffer,"QUIT",4) == 0)
143                 break;
144             write(sock,buffer,n);
145         }
146
147         if (shutdown(sock, SHUT_RDWR) < 0) fail("server_v6","shutdown()");
148         if (close(sock) < 0) fail("server_v6","close()");
149         if (close(serv) < 0) fail("server_v6","close()");
150     }
151
152 }
153
154 BOOST_AUTO_UNIT_TEST(tcpv4ClientSocketHandle)
155 {
156     {
157         senf::TCPv4ClientSocketHandle sock;
158
159         BOOST_CHECK_THROW( sock.connect(senf::INet4Address("127.0.0.1:12345")), senf::SystemException );
160         BOOST_CHECK_THROW( sock.protocol().connect("127.0.0.1:12345"), senf::SystemException );
161     }
162
163     try {
164         alarm(10);
165         start(server_v4);
166         senf::TCPv4ClientSocketHandle sock;
167         BOOST_CHECK_NO_THROW( sock.bind("127.0.0.1:23456") );
168         BOOST_CHECK_NO_THROW( sock.connect("127.0.0.1:12345") );
169         BOOST_CHECK( sock.peer() == "127.0.0.1:12345" );
170         BOOST_CHECK( sock.local() == "127.0.0.1:23456" );
171         BOOST_CHECK( sock.blocking() );
172         BOOST_CHECK_NO_THROW( sock.rcvbuf(2048) );
173         BOOST_CHECK_EQUAL( sock.rcvbuf(), 2048u );
174         BOOST_CHECK_NO_THROW( sock.sndbuf(2048) );
175         BOOST_CHECK_EQUAL( sock.sndbuf(), 2048u );
176         BOOST_CHECK_NO_THROW( sock.write("TEST-WRITE") );
177         BOOST_CHECK_EQUAL( sock.read(), "TEST-WRITE" );
178         BOOST_CHECK( !sock.eof() );
179         sock.write("QUIT");
180         sleep(1);
181         stop();
182         sleep(1);
183         BOOST_CHECK_EQUAL( sock.read(), "" );
184         BOOST_CHECK( sock.eof() );
185         BOOST_CHECK( !sock );
186         alarm(0);
187     } catch (...) {
188         alarm(0);
189         sleep(1);
190         stop();
191         sleep(1);
192         throw;
193     }
194
195     {
196         senf::TCPv4ClientSocketHandle sock;
197
198         // Since this is a TCP socket, most of the calls will fail or
199         // are at least not sensible ...
200         // I'll have to move those to a UDPSocket test ... they should
201         // realy only be in the UDP Protocol implementation
202 //        BOOST_CHECK_NO_THROW( sock.protocol().mcTTL() );
203 //        BOOST_CHECK_THROW( sock.protocol().mcTTL(1), senf::SystemException );
204 //        BOOST_CHECK_NO_THROW( sock.protocol().mcLoop() );
205 //        BOOST_CHECK_NO_THROW( sock.protocol().mcLoop(false) );
206 //        BOOST_CHECK_NO_THROW( sock.protocol().mcAddMembership("224.0.0.1:0") );
207 //        BOOST_CHECK_NO_THROW( sock.protocol().mcAddMembership("224.0.0.1:0","127.0.0.1:0") );
208 //        BOOST_CHECK_NO_THROW( sock.protocol().mcDropMembership("224.0.0.1:0","127.0.0.1:0") );
209 //        BOOST_CHECK_NO_THROW( sock.protocol().mcDropMembership("224.0.0.1:0") );
210 //        BOOST_CHECK_THROW( sock.protocol().mcIface("lo"), senf::SystemException );
211
212         // The following setsockopts are hard to REALLY test ...
213         BOOST_CHECK_NO_THROW( sock.protocol().nodelay(true) );
214         BOOST_CHECK( sock.protocol().nodelay() );
215         BOOST_CHECK_EQUAL( sock.protocol().siocinq(), 0u );
216         BOOST_CHECK_EQUAL( sock.protocol().siocoutq(), 0u );
217
218         BOOST_CHECK_NO_THROW( sock.protocol().reuseaddr(true) );
219         BOOST_CHECK( sock.protocol().reuseaddr() );
220         BOOST_CHECK_NO_THROW( sock.protocol().linger(true,0) );
221         BOOST_CHECK( sock.protocol().linger() == std::make_pair(true, 0u) );
222     }
223 }
224
225 BOOST_AUTO_UNIT_TEST(tcpv6ClientSocketHandle)
226 {
227     {
228         senf::TCPv6ClientSocketHandle sock;
229
230         BOOST_CHECK_THROW( sock.connect(senf::INet6SocketAddress("[::1]:12345")), senf::SystemException );
231         BOOST_CHECK_THROW( sock.protocol().connect("[::1]:12345"), senf::SystemException );
232     }
233
234     try {
235         alarm(10);
236         start(server_v6);
237         senf::TCPv6ClientSocketHandle sock;
238         BOOST_CHECK_NO_THROW( sock.bind("[::1]:23456") );
239         BOOST_CHECK_NO_THROW( sock.connect("[::1]:12345") );
240         BOOST_CHECK( sock.peer() == "[::1]:12345" );
241         BOOST_CHECK( sock.local() == "[::1]:23456" );
242         BOOST_CHECK( sock.blocking() );
243         BOOST_CHECK_NO_THROW( sock.rcvbuf(2048) );
244         BOOST_CHECK_EQUAL( sock.rcvbuf(), 2048u );
245         BOOST_CHECK_NO_THROW( sock.sndbuf(2048) );
246         BOOST_CHECK_EQUAL( sock.sndbuf(), 2048u );
247         BOOST_CHECK_NO_THROW( sock.write("TEST-WRITE") );
248         BOOST_CHECK_EQUAL( sock.read(), "TEST-WRITE" );
249         // this fails with ENOFILE ... why ????
250         // BOOST_CHECK_NO_THROW( sock.protocol().timestamp() );
251         BOOST_CHECK( !sock.eof() );
252         sock.write("QUIT");
253         sleep(1);
254         stop();
255         sleep(1);
256         BOOST_CHECK_EQUAL( sock.read(), "" );
257         BOOST_CHECK( sock.eof() );
258         BOOST_CHECK( !sock );
259         alarm(0);
260     } catch (...) {
261         alarm(0);
262         sleep(1);
263         stop();
264         sleep(1);
265         throw;
266     }
267
268     {
269         senf::TCPv6ClientSocketHandle sock;
270
271         // The following setsockopts are hard to REALLY test ...
272         BOOST_CHECK_NO_THROW( sock.protocol().nodelay(true) );
273         BOOST_CHECK( sock.protocol().nodelay() );
274         BOOST_CHECK_EQUAL( sock.protocol().siocinq(), 0u );
275         BOOST_CHECK_EQUAL( sock.protocol().siocoutq(), 0u );
276
277         BOOST_CHECK_NO_THROW( sock.protocol().reuseaddr(true) );
278         BOOST_CHECK( sock.protocol().reuseaddr() );
279         BOOST_CHECK_NO_THROW( sock.protocol().linger(true,0) );
280         BOOST_CHECK( sock.protocol().linger() == std::make_pair(true, 0u) );
281     }
282 }
283
284 ///////////////////////////////////////////////////////////////////////////
285
286 namespace {
287
288     void client_v4()
289     {
290         int sock = socket(PF_INET,SOCK_STREAM,0);
291         if (sock<0) fail("client_v4","socket()");
292         struct sockaddr_in sin;
293         ::memset(&sin,0,sizeof(sin));
294         sin.sin_family = AF_INET;
295         sin.sin_port = htons(12346);
296         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
297         if (connect(sock,(struct sockaddr *)&sin,sizeof(sin)) < 0)
298             fail("client_v4","connect()");
299
300         char buffer[1024];
301         while (1) {
302             int n = read(sock,buffer,1024);
303             if (n == 4 && strncmp(buffer,"QUIT",4) == 0)
304                 break;
305             write(sock,buffer,n);
306         }
307
308         if (shutdown(sock, SHUT_RDWR) < 0) fail("client_v4","shutdown()");
309         if (close(sock) < 0) fail("client_v4","close()");
310     }
311
312     void client_v6()
313     {
314         int sock = socket(PF_INET6,SOCK_STREAM,0);
315         if (sock<0) fail("client_v6","socket()");
316         struct sockaddr_in6 sin;
317         ::memset(&sin,0,sizeof(sin));
318         sin.sin6_family = AF_INET6;
319         sin.sin6_port = htons(12347);
320         sin.sin6_addr = in6addr_loopback;
321         if (connect(sock,(struct sockaddr *)&sin,sizeof(sin)) < 0)
322             fail("client_v6","connect()");
323
324         char buffer[1024];
325         while (1) {
326             int n = read(sock,buffer,1024);
327             if (n == 4 && strncmp(buffer,"QUIT",4) == 0)
328                 break;
329             write(sock,buffer,n);
330         }
331
332         if (shutdown(sock, SHUT_RDWR) < 0) fail("client_v6","shutdown()");
333         if (close(sock) < 0) fail("client_v6","close()");
334     }
335
336 }
337
338 BOOST_AUTO_UNIT_TEST(tcpv4ServerSocketHandle)
339 {
340     try {
341         alarm(10);
342         BOOST_CHECKPOINT("Opening server socket");
343         senf::TCPv4ServerSocketHandle server ("127.0.0.1:12346");
344         BOOST_CHECKPOINT("Starting client");
345         start(client_v4);
346
347         BOOST_CHECKPOINT("Accepting connection");
348         senf::TCPv4ClientSocketHandle client = server.accept();
349         BOOST_CHECK_NO_THROW(client.write("QUIT"));
350
351         BOOST_CHECKPOINT("Stopping client");
352         sleep(1);
353         stop();
354         alarm(0);
355     } catch (...) {
356         alarm(0);
357         sleep(1);
358         stop();
359         sleep(1);
360         throw;
361     }
362 }
363
364 BOOST_AUTO_UNIT_TEST(tcpv6ServerSocketHandle)
365 {
366     try {
367         alarm(10);
368         BOOST_CHECKPOINT("Opening server socket");
369         senf::TCPv6ServerSocketHandle server ("[::1]:12347");
370         BOOST_CHECKPOINT("Starting client");
371         start(client_v6);
372
373         BOOST_CHECKPOINT("Accepting connection");
374         senf::TCPv6ClientSocketHandle client = server.accept();
375         BOOST_CHECK_NO_THROW(client.write("QUIT"));
376
377         BOOST_CHECKPOINT("Stopping client");
378         sleep(1);
379         stop();
380         alarm(0);
381     } catch (...) {
382         alarm(0);
383         sleep(1);
384         stop();
385         sleep(1);
386         throw;
387     }
388 }
389
390 ///////////////////////////////cc.e////////////////////////////////////////
391 #undef prefix_
392
393 \f
394 // Local Variables:
395 // mode: c++
396 // fill-column: 100
397 // c-file-style: "senf"
398 // indent-tabs-mode: nil
399 // ispell-local-dictionary: "american"
400 // compile-command: "scons -u test"
401 // comment-column: 40
402 // End: