1 /*******************************************************************************
2
3 Test-suite for UnixSockets.
4
5 The tests involve unix sockets and forking processes.
6
7 FLAKY: the unittests in this module are very flaky, as they rely on making
8 various system calls (fork(), waitpid(), epoll_wait(), epoll_ctl(), etc)
9 which could, under certain environmental conditions, fail.
10
11 Copyright:
12 Copyright (c) 2016-2017 dunnhumby Germany GmbH.
13 All rights reserved.
14
15 License:
16 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17 Alternatively, this file may be distributed under the terms of the Tango
18 3-Clause BSD License (see LICENSE_BSD.txt for details).
19
20 *******************************************************************************/
21
22 module integrationtest.selectlistener.main;
23
24 import ocean.meta.types.Qualifiers;
25
26 import ocean.core.Enforce: enforce;
27 import Ocean = ocean.core.Test;
28 import ocean.io.select.EpollSelectDispatcher;
29 import ocean.time.timeout.TimeoutManager;
30 import ocean.stdc.posix.sys.un;
31 import ocean.sys.socket.UnixSocket;
32
33 import core.stdc.errno: ECONNREFUSED;
34 import core.stdc.stdlib;
35 import core.sys.posix.unistd: fork, unlink;
36 import core.sys.posix.sys.socket;
37 import core.sys.posix.sys.types : pid_t;
38 import core.sys.posix.sys.wait: waitpid;
39 import core.thread;
40 import core.time;
41
42 import integrationtest.selectlistener.UnixServer;
43
44
45 /*******************************************************************************
46
47 Opens a client connection and issues a request. The reply must be exactly
48 what was sent.
49
50 Params:
51 socket_path = the unix socket path.
52
53 Returns:
54 0 on success
55
56 Throws:
57 an exception if something goes wrong (could not connect, wrong reply, etc)
58
59 *******************************************************************************/
60
61 int run_client( istring socket_path)
62 {
63 auto local_address = sockaddr_un.create(socket_path);
64 auto client = new UnixSocket();
65
66 scope (exit) client.close();
67
68 auto socket_fd = client.socket();
69 enforce(socket_fd >= 0, "socket() call failed!");
70
71 auto connect_result = client.connect(&local_address);
72
73 istring str1 = "HELLO, ... !\n";
74
75 client.write(str1);
76
77 auto read_buffer = new char[str1.length + 1];
78 read_buffer[] = 0;
79 auto buff = cast(void[])read_buffer;
80
81 auto read_bytes = client.recv(buff, 0);
82 enforce(read_bytes > 0);
83
84 read_buffer.length = read_bytes;
85
86 Ocean.test(read_buffer == str1);
87
88 return 0;
89 }
90
91 /*******************************************************************************
92
93 Makes a test. Starts the client in its own process, then starts the server
94 in the current process and waits for them to finish.
95 The client sends a message, gets an answer, compares the two strings and
96 exits.
97 The server opens the socket, accepts one request, reads the message,
98 replies with the same message and exits (no more requests are handled).
99
100 Params:
101 socket_path = the unix socket path.
102
103 Throws:
104 an exception if something goes wrong
105
106 *******************************************************************************/
107
108 void run_test ( istring socket_path )
109 {
110 auto timeout_mgr = new TimeoutManager;
111 auto epoll = new EpollSelectDispatcher(timeout_mgr);
112
113 unlink(socket_path.ptr);
114 auto local_address = sockaddr_un.create(socket_path);
115 auto unix_socket = new UnixSocket;
116 auto unix_server = new UnixServer(cast(sockaddr*)&local_address,
117 unix_socket, epoll);
118 epoll.register(unix_server);
119
120 // UnixServer should already bind and listen at this point,
121 // so it is safe to connect from the client
122 pid_t pid = fork();
123
124 enforce(pid != -1);
125
126 if (pid == 0) // child
127 {
128 run_client(socket_path);
129 _Exit(0);
130 }
131
132 epoll.eventLoop();
133
134 enforce((socket_path[0] == '\0') || (unlink(socket_path.ptr) == 0),
135 "Can't remove socket file.");
136
137 int status;
138 waitpid(pid, &status, 0);
139 enforce(status == 0, "Child exit status should be 0");
140 }
141
142 /*******************************************************************************
143
144 Makes two tests, one with standard UNIX socket and one with (LINUX) abstract
145 namespace sockets.
146
147 *******************************************************************************/
148
149 version (unittest) {} else
150 int main ( )
151 {
152 run_test("/tmp/ocean_socket_test");
153 run_test("\0ocean_socket_test");
154
155 return 0;
156 }