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.transition; 25 26 import ocean.core.Enforce: enforce; 27 import Ocean = ocean.core.Test; 28 import ocean.core.Time: seconds; 29 import ocean.io.select.EpollSelectDispatcher; 30 import core.stdc.errno: ECONNREFUSED; 31 import ocean.stdc.posix.sys.types : pid_t; 32 import ocean.stdc.posix.sys.wait: waitpid; 33 import core.sys.posix.unistd: fork, unlink; 34 import ocean.time.timeout.TimeoutManager; 35 import ocean.sys.socket.UnixSocket; 36 import ocean.stdc.posix.sys.un; 37 import core.sys.posix.sys.socket; 38 import core.stdc.stdlib; 39 import core.thread; 40 41 import integrationtest.selectlistener.UnixServer; 42 43 44 /******************************************************************************* 45 46 Opens a client connection and issues a request. The reply must be exactly 47 what was sent. 48 49 Params: 50 socket_path = the unix socket path. 51 52 Returns: 53 0 on success 54 55 Throws: 56 an exception if something goes wrong (could not connect, wrong reply, etc) 57 58 *******************************************************************************/ 59 60 int run_client( istring socket_path) 61 { 62 auto local_address = sockaddr_un.create(socket_path); 63 auto client = new UnixSocket(); 64 65 scope (exit) client.close(); 66 67 auto socket_fd = client.socket(); 68 enforce(socket_fd >= 0, "socket() call failed!"); 69 70 auto connect_result = client.connect(&local_address); 71 72 istring str1 = "HELLO, ... !\n"; 73 74 client.write(str1); 75 76 auto read_buffer = new char[str1.length + 1]; 77 read_buffer[] = 0; 78 auto buff = cast(void[])read_buffer; 79 80 auto read_bytes = client.recv(buff, 0); 81 enforce(read_bytes > 0); 82 83 read_buffer.length = read_bytes; 84 85 Ocean.test(read_buffer == str1); 86 87 return 0; 88 } 89 90 /******************************************************************************* 91 92 Makes a test. Starts the client in its own process, then starts the server 93 in the current process and waits for them to finish. 94 The client sends a message, gets an answer, compares the two strings and 95 exits. 96 The server opens the socket, accepts one request, reads the message, 97 replies with the same message and exits (no more requests are handled). 98 99 Params: 100 socket_path = the unix socket path. 101 102 Throws: 103 an exception if something goes wrong 104 105 *******************************************************************************/ 106 107 void run_test ( istring socket_path ) 108 { 109 auto timeout_mgr = new TimeoutManager; 110 auto epoll = new EpollSelectDispatcher(timeout_mgr); 111 112 unlink(socket_path.ptr); 113 auto local_address = sockaddr_un.create(socket_path); 114 auto unix_socket = new UnixSocket; 115 auto unix_server = new UnixServer(cast(sockaddr*)&local_address, 116 unix_socket, epoll); 117 epoll.register(unix_server); 118 119 // UnixServer should already bind and listen at this point, 120 // so it is safe to connect from the client 121 pid_t pid = fork(); 122 123 enforce(pid != -1); 124 125 if (pid == 0) // child 126 { 127 run_client(socket_path); 128 _Exit(0); 129 } 130 131 epoll.eventLoop(); 132 133 enforce((socket_path[0] == '\0') || (unlink(socket_path.ptr) == 0), 134 "Can't remove socket file."); 135 136 int status; 137 waitpid(pid, &status, 0); 138 enforce(status == 0, "Child exit status should be 0"); 139 } 140 141 /******************************************************************************* 142 143 Makes two tests, one with standard UNIX socket and one with (LINUX) abstract 144 namespace sockets. 145 146 *******************************************************************************/ 147 148 version(UnitTest) {} else 149 int main ( ) 150 { 151 run_test("/tmp/ocean_socket_test"); 152 run_test("\0ocean_socket_test"); 153 154 return 0; 155 }