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 }