Skip to content
Snippets Groups Projects
SimpleDiskAndNetworkTest.cpp 4.76 KiB
Newer Older
  • Learn to ignore specific revisions
  • // SPDX-License-Identifier: LGPL-3.0-or-later
    // Copyright © 2020-2021 Florian Fischer
    #include <arpa/inet.h>								// for inet_addr
    #include <bits/types/struct_iovec.h>	// for iovec
    #include <fcntl.h>										// for open, O_RDONLY
    #include <netinet/in.h>								// for sockaddr_in, htons, in_addr
    #include <sys/socket.h>								// for bind, listen, setsockopt
    #include <unistd.h>										// for close
    
    #include <cassert>	// for assert
    #include <cstdlib>	// for mkstemp, exit, EXIT_SUCCESS
    #include <cstring>	// for memcmp, memset
    #include <memory>		// for allocator, unique_ptr
    #include <string>		// for string
    #include <vector>		// for vector
    
    #include "Common.hpp"										 // for DIE_MSG_ERRNO, DIE_MSG
    #include "CountingPrivateSemaphore.hpp"	 // for CPS
    #include "emper.hpp"										 // for spawn
    #include "fixtures/network.hpp"					 // for echo_client
    #include "io.hpp"												 // for readFile, accept, recv, send
    #include "io/Future.hpp"								 // for Future
    
    #define PORT 4243
    #define MAX 1024
    
    static void server_func(int sockfd) {
    
    	struct sockaddr_in clientaddr;
    	socklen_t clientaddr_len = sizeof(clientaddr);
    
    	auto client_fd = emper::io::acceptAndWait(sockfd, reinterpret_cast<struct sockaddr*>(&clientaddr),
    																						&clientaddr_len);
    
    
    	if (client_fd < 0) {
    		DIE_MSG_ERRNO("accept failed");
    	}
    
    	// NOLINTNEXTLINE(modernize-avoid-c-arrays)
    	char recv_buf[MAX];
    	// NOLINTNEXTLINE(modernize-avoid-c-arrays)
    	char read_buf[MAX];
    
    	for (;;) {
    
    		ssize_t received = emper::io::recvAndWait(client_fd, recv_buf, sizeof(recv_buf), 0);
    
    		if (received == 0) {
    			exit(EXIT_SUCCESS);
    		}
    
    		if (received == -1) {
    			DIE_MSG_ERRNO("recv failed");
    			break;
    		}
    
    		// NOLINTNEXTLINE(modernize-avoid-c-arrays)
    		char file_name[] = "/tmp/emper-SimpleDiskAndNetworkTestFile-XXXXXX";
    		int file_fd = mkstemp(file_name);
    		if (file_fd == -1) {
    			DIE_MSG_ERRNO("mkstemp failed");
    		}
    
    
    		ssize_t written = emper::io::writeFileAndWait(file_fd, recv_buf, received);
    
    		if (written < 0) {
    			DIE_MSG_ERRNO("write failed");
    		}
    		close(file_fd);
    
    
    		file_fd = emper::io::openAndWait(file_name, O_RDONLY);
    
    		if (file_fd == -1) {
    			DIE_MSG_ERRNO("open failed");
    		}
    
    
    		ssize_t bytes_read = emper::io::readFileAndWait(file_fd, read_buf, written);
    
    		if (bytes_read == 0) {
    			DIE_MSG("nothing to read");
    		}
    
    		if (bytes_read < 0) {
    			DIE_MSG_ERRNO("read failed");
    		}
    		close(file_fd);
    
    
    		ssize_t sent = emper::io::sendAndWait(client_fd, read_buf, bytes_read, 0);
    
    		if (sent == 0) {
    			DIE_MSG("client socket unexpected shutdown");
    		}
    
    		if (sent == -1) {
    			DIE_MSG_ERRNO("send failed");
    		}
    
    		// NOLINTNEXTLINE(modernize-avoid-c-arrays)
    		char file2_name[] = "/tmp/emper-SimpleDiskAndNetworkTestFile-XXXXXX";
    		file_fd = mkstemp(file2_name);
    		if (file_fd == -1) {
    			DIE_MSG_ERRNO("mkstemp failed");
    		}
    
    		const int iovcnt = 2;
    		// NOLINTNEXTLINE(modernize-avoid-c-arrays)
    		struct iovec iov[iovcnt];
    
    		std::string s1 = "foo";
    		std::string s2 = "bar";
    
    		iov[0].iov_base = (void*)s1.c_str();
    		iov[0].iov_len = s1.length();
    		iov[1].iov_base = (void*)s2.c_str();
    		iov[1].iov_len = s2.length();
    
    		auto writevFuture = emper::io::writev(file_fd, &iov[0], iovcnt);
    
    		written = writevFuture->waitAndSetErrno();
    
    		if (written < 0) {
    			DIE_MSG_ERRNO("wrtev failed");
    		}
    		close(file_fd);
    
    
    		file_fd = emper::io::openAndWait(file2_name, O_RDONLY);
    
    		if (file_fd == -1) {
    			DIE_MSG_ERRNO("open failed");
    		}
    
    		auto readFuture = emper::io::readFile(file_fd, read_buf, written, 0, true);
    
    		bytes_read = readFuture->waitAndSetErrno();
    
    		if (bytes_read == 0) {
    			DIE_MSG("nothing to read");
    		}
    
    		if (bytes_read < 0) {
    			DIE_MSG_ERRNO("read failed");
    		}
    
    		assert(written == bytes_read);
    
    		assert(memcmp(read_buf, iov[0].iov_base, iov[0].iov_len) == 0);
    		assert(memcmp((char*)read_buf + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len) == 0);
    
    		close(file_fd);
    	}
    }
    
    void emperTest() {
    
    	int sockfd;
    	struct sockaddr_in servaddr;
    
    	// socket creation and verification
    	sockfd = socket(AF_INET, SOCK_STREAM, 0);
    	if (sockfd == -1) {
    		DIE_MSG_ERRNO("socket creation failed");
    	}
    	memset(&servaddr, 0, sizeof(servaddr));
    
    	// assign IP, PORT
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    	servaddr.sin_port = htons(PORT);
    
    	int reuseaddr = 1;
    	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) {
    		DIE_MSG_ERRNO("setsockopt failed");
    	}
    	if (bind(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
    		DIE_MSG_ERRNO("bind failed");
    	}
    	if (listen(sockfd, 1) != 0) {
    		DIE_MSG_ERRNO("listen failed");
    	}
    
    
    	spawn([=] { server_func(sockfd); }, cps);
    
    
    	spawn(
    			[] {
    				const std::vector<std::string> msgs{"foo", "bar"};
    
    				std::string port = std::to_string(PORT);
    				std::string host("127.0.0.1");
    				echo_client(host, port, msgs);