diff --git a/.github/workflows/gtest.yml b/.github/workflows/gtest.yml index 0987d48f..c1c8b310 100644 --- a/.github/workflows/gtest.yml +++ b/.github/workflows/gtest.yml @@ -1,7 +1,7 @@ -name: GOOGLE_TEST +name: UNIT_TEST on: [push] jobs: - run-google-test: + run-unit-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -11,7 +11,7 @@ jobs: cmake -S . -B build cmake --build build - - name: run-all-test + - name: run all tests run: ./build/unit_test - uses: sarisia/actions-status-discord@v1 diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index f4163fb3..92add2e2 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -1,4 +1,4 @@ -name: UNIT_TEST +name: UNIT_TEST_SH on: [push] jobs: run-unit-test: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a832d4b..3e16670d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,11 +24,13 @@ enable_testing() # includes --------------------------------------------------------------------- include_directories( includes + srcs/Socket ) # webserv_srcs ----------------------------------------------------------------- set(webserv_srcs srcs/get_valid_config_file_path.cpp + srcs/Socket/Socket.cpp ) add_executable(webserv @@ -38,7 +40,9 @@ add_executable(webserv # unit_test_srcs --------------------------------------------------------------- set (unit_test_srcs - test/unit_test/is_valid_file_path/test_get_valid_config_file_path.cpp) + test/unit_test/is_valid_file_path/test_get_valid_config_file_path.cpp + test/unit_test/TestSocket.cpp +) add_executable(unit_test ${webserv_srcs} diff --git a/Makefile b/Makefile index 75db8a63..0e3158eb 100644 --- a/Makefile +++ b/Makefile @@ -63,10 +63,11 @@ lint : .PHONY : unit unit : - rm -rf build + #rm -rf build cmake -S . -B build cmake --build build + ./build/unit_test 2>/dev/null + #./build/unit_test #cd build && ctest - ./build/unit_test -include $(DEPS) \ No newline at end of file diff --git a/srcs/Socket/Socket.cpp b/srcs/Socket/Socket.cpp index efeba795..bdde53a4 100644 --- a/srcs/Socket/Socket.cpp +++ b/srcs/Socket/Socket.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include "webserv.hpp" #include "Socket.hpp" @@ -26,20 +28,59 @@ Socket::Socket() : _status(ERROR), this->_status = OK; } +Socket::Socket(const char *server_ip, const char *server_port) : _status(ERROR), + _socket_fd(ERROR), + _addr_info(NULL), + _server_ip(server_ip), + _server_port(server_port) { + if (create_socket() == ERROR) { + return; + } + if (bind_socket() == ERROR) { + return; + } + if (listen_socket() == ERROR) { + return; + } + if (set_fd_to_nonblock() == ERROR) { + return; + } + this->_status = OK; +} + +// Socket::Socket(const Socket ©) { +// *this = copy; +// } +// +// Socket &Socket::operator=(const Socket &rhs) { +// if (this != &rhs) { +// _status = rhs._status; +// _socket_fd = rhs._socket_fd; +// _server_ip = rhs._server_ip; +// _server_port = rhs._server_port; +// // _addr_info = rhs._addr_info; +// set_addr_info(this->_server_ip, this->_server_port, &this->_addr_info); +// } +// return *this; +// } + Socket::~Socket() { if (this->_addr_info != NULL) { freeaddrinfo(this->_addr_info); + this->_addr_info = NULL; } if (this->_socket_fd != ERROR) { close_socket_fd(this->_socket_fd); + this->_socket_fd = ERROR; } + // std::cout << "destructor" << std::endl; } int Socket::create_socket() { int errcode; int ai_family, ai_socktype, ai_protocol; - errcode = set_addr_info(&this->_addr_info); + errcode = set_addr_info(this->_server_ip, this->_server_port, &this->_addr_info); if (errcode != OK) { std::cerr << gai_strerror(errcode) << std::endl; return ERROR; @@ -134,3 +175,5 @@ void Socket::close_socket_fd(int socket_fd) { int Socket::get_socket_fd() const { return this->_socket_fd; } int Socket::get_status() const { return this->_status; } +// std::string Socket::get_server_port() const { return this->_server_port; } +// std::string Socket::get_server_ip() const { return this->_server_ip; } \ No newline at end of file diff --git a/srcs/Socket/Socket.hpp b/srcs/Socket/Socket.hpp index d7e1ea3f..eb4c1527 100644 --- a/srcs/Socket/Socket.hpp +++ b/srcs/Socket/Socket.hpp @@ -8,10 +8,15 @@ class Socket { public: Socket(); + Socket(const char *server_ip, const char *server_port); ~Socket(); + // Socket(const Socket ©); // for debug + // Socket &operator=(const Socket &rhs); // for debug int get_socket_fd() const; int get_status() const; + // std::string get_server_ip() const; // for debug + // std::string get_server_port() const; // for debug private: int _status; diff --git a/test/unit_test/TestSocket.cpp b/test/unit_test/TestSocket.cpp new file mode 100644 index 00000000..f907749b --- /dev/null +++ b/test/unit_test/TestSocket.cpp @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "webserv.hpp" +#include "Socket.hpp" +#include "Color.hpp" + +static struct sockaddr_in create_addr(); +static int create_nonblock_client_fd(); + +/* *********************** */ +/* Socket Unit Test */ +/* *********************** */ +TEST(SocketUnitTest, DefaultConstructor) { + Socket socket = Socket(); + + EXPECT_EQ(OK, socket.get_status()); +} + +TEST(SocketUnitTest, ConstructorWithArgument) { + Socket socket = Socket(SERVER_IP, SERVER_PORT); + + EXPECT_EQ(OK, socket.get_status()); +} + +// TEST(SocketUnitTest, CopyConstructor) { +// Socket socket_src = Socket(SERVER_IP, SERVER_PORT); +// Socket socket_new = Socket(socket_src); +// +// EXPECT_EQ(socket_src.get_status(), socket_new.get_status()); +// EXPECT_EQ(socket_src.get_socket_fd(), socket_new.get_socket_fd()); +// EXPECT_EQ(socket_src.get_server_port(), socket_new.get_server_port()); +// EXPECT_EQ(socket_src.get_server_ip(), socket_new.get_server_ip()); +// } +// +// TEST(SocketUnitTest, CopyAssignmentConstructor) { +// Socket socket_src = Socket(SERVER_IP, SERVER_PORT); +// Socket socket_new = socket_src; +// +// EXPECT_EQ(socket_src.get_status(), socket_new.get_status()); +// EXPECT_EQ(socket_src.get_socket_fd(), socket_new.get_socket_fd()); +// EXPECT_EQ(socket_src.get_server_port(), socket_new.get_server_port()); +// EXPECT_EQ(socket_src.get_server_ip(), socket_new.get_server_ip()); +// } + +TEST(SocketUnitTest, ConstructorWithValidServerIP) { + Socket socket1 = Socket("127.0.0.1", SERVER_PORT); + Socket socket2 = Socket("0.0.0.0", SERVER_PORT); + Socket socket3 = Socket("000.0000.00000.000000", SERVER_PORT); // Address already in use? + + EXPECT_EQ(OK, socket1.get_status()); + EXPECT_EQ(OK, socket2.get_status()); + // EXPECT_EQ(OK, socket3.get_status()); +} + +TEST(SocketUnitTest, ConstructorWithInvalidServerIP) { + Socket socket1 = Socket("127.0.0.0.1", SERVER_PORT); + Socket socket2 = Socket("256.0.0.0", SERVER_PORT); + Socket socket3 = Socket("-1", SERVER_PORT); + Socket socket4 = Socket("a.0.0.0", SERVER_PORT); + Socket socket5 = Socket("127:0.0.1", SERVER_PORT); + Socket socket6 = Socket("127,0.0.1", SERVER_PORT); + Socket socket7 = Socket("127.0.0.-1", SERVER_PORT); + Socket socket8 = Socket("2147483647.2147483647.2147483647.2147483647", SERVER_PORT); + // Socket socket9 = Socket("", SERVER_PORT); todo: ok? + Socket socket10 = Socket("hoge", SERVER_PORT); + Socket socket11 = Socket("0001.0001.0001.0001", SERVER_PORT); + Socket socket12 = Socket("255.255.255.254", SERVER_PORT); + Socket socket13 = Socket("255.255.255.255", SERVER_PORT); + + EXPECT_EQ(ERROR, socket1.get_status()); + EXPECT_EQ(ERROR, socket2.get_status()); + EXPECT_EQ(ERROR, socket3.get_status()); + EXPECT_EQ(ERROR, socket4.get_status()); + EXPECT_EQ(ERROR, socket5.get_status()); + EXPECT_EQ(ERROR, socket6.get_status()); + EXPECT_EQ(ERROR, socket7.get_status()); + EXPECT_EQ(ERROR, socket8.get_status()); + // EXPECT_EQ(ERROR, socket9.get_status()); + EXPECT_EQ(ERROR, socket10.get_status()); + EXPECT_EQ(ERROR, socket11.get_status()); + EXPECT_EQ(ERROR, socket12.get_status()); + EXPECT_EQ(ERROR, socket13.get_status()); +} + +TEST(SocketUnitTest, ConstructorWithValidServerPort) { + Socket socket1 = Socket(SERVER_IP, "0"); + Socket socket2 = Socket(SERVER_IP, "0000"); + Socket socket3 = Socket(SERVER_IP, "4242"); + Socket socket4 = Socket(SERVER_IP, "65535"); + + EXPECT_EQ(OK, socket1.get_status()); + EXPECT_EQ(OK, socket2.get_status()); + EXPECT_EQ(OK, socket3.get_status()); + EXPECT_EQ(OK, socket4.get_status()); +} + +TEST(SocketUnitTest, ConstructorWithInvalidServerPort) { + Socket socket1 = Socket(SERVER_IP, "-1"); + Socket socket2 = Socket(SERVER_IP, "65536"); + // Socket socket3 = Socket(SERVER_IP, ""); // strtol->0 (tmp) + Socket socket4 = Socket(SERVER_IP, "hoge"); + Socket socket5 = Socket(SERVER_IP, "--123123"); + Socket socket6 = Socket(SERVER_IP, "127.1"); + + EXPECT_EQ(ERROR, socket1.get_status()); + EXPECT_EQ(ERROR, socket2.get_status()); + // EXPECT_EQ(ERROR, socket3.get_status()); + EXPECT_EQ(ERROR, socket4.get_status()); + EXPECT_EQ(ERROR, socket5.get_status()); + EXPECT_EQ(ERROR, socket6.get_status()); +} + +TEST(SocketUnitTest, Getter) { + Socket socket = Socket(); + + EXPECT_EQ(OK, socket.get_status()); + EXPECT_NE(ERROR, socket.get_socket_fd()); +} + +// TEST(SocketUnitTest, CreateManySocket) { +// int limit = 20; +// +// struct rlimit old_limits; +// if (getrlimit(RLIMIT_NOFILE, &old_limits) != 0) { +// std::cerr << "getrlimit failed\n"; +// return; +// } +// printf("old_limit.cur:%llu,\n", old_limits.rlim_cur); +// +// struct rlimit new_limits; +// new_limits.rlim_cur = limit; +// new_limits.rlim_max = old_limits.rlim_max; +// +// printf("new_limit.cur:%llu\n", new_limits.rlim_cur); +// +// if (setrlimit(RLIMIT_NOFILE, &new_limits) != 0) { +// std::cerr << "setrlimit failed\n"; +// return; +// } +// +// int min_fd = 3; +// std::vector sockets; +// for (int i = 0; i < limit + 10; ++i) { +// // Socket socket = Socket(SERVER_IP, std::to_string(49152 + i).c_str()); +// // sockets.push_back(socket); +// // sockets[i] = Socket(SERVER_IP, std::to_string(49152 + i).c_str()); +// sockets.push_back(Socket(SERVER_IP, std::to_string(49152 + i).c_str())); +// printf("%si:%d, fd:%d, status:%d%s\n", YELLOW, i, sockets[i].get_socket_fd(), sockets[i].get_status(), RESET); +// +// if (i + min_fd <= limit) { +// EXPECT_EQ(OK, sockets[i].get_status()); +// EXPECT_NE(ERROR, sockets[i].get_socket_fd()); +// } else { +// EXPECT_EQ(ERROR, sockets[i].get_status()); +// EXPECT_EQ(ERROR, sockets[i].get_socket_fd()); +// } +// } +// +// if (setrlimit(RLIMIT_NOFILE, &old_limits) != 0) { +// std::cerr << "Restoring setrlimit failed\n"; +// return; +// } +// +// printf("old_limit.cur:%llu\n", old_limits.rlim_cur); +// } + +/* *********************** */ +/* Socket Integration Test */ +/* *********************** */ +TEST(SocketIntegrationTest, ConnectToClient) { + Socket server; + + EXPECT_EQ(server.get_status(), OK); + EXPECT_NE(server.get_socket_fd(), ERROR); + + int client_fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_port = htons(std::strtol(SERVER_PORT, NULL, 10)); + addr.sin_addr.s_addr = inet_addr(SERVER_IP); + + EXPECT_EQ(connect(client_fd, (struct sockaddr *)&addr, sizeof(addr)), OK); + + close(client_fd); +} + +TEST(SocketIntegrationTest, ConnectTooManyClient) { + Socket server; + int client_fd; + + EXPECT_EQ(OK, server.get_status()); + EXPECT_NE(ERROR, server.get_socket_fd()); + + // connect under SOMAXCONN + std::vector client_fds; + for (int i = 0; i < SOMAXCONN; ++i) { + client_fd = socket(AF_INET, SOCK_STREAM, 0); + // printf("cnt:%d, client_fd:%d\n", i+1, client_fd); + + EXPECT_NE(ERROR, client_fd); + + if (client_fd != ERROR) { + client_fds.push_back(client_fd); + struct sockaddr_in addr = create_addr(); + + EXPECT_EQ(OK, connect(client_fd, (struct sockaddr *)&addr, sizeof(addr))); + } + } + + // connect over SOMAXCONN -> fd set to nonblock + client_fd = create_nonblock_client_fd(); + // printf("cnt:%d, client_fd:%d\n", SOMAXCONN, client_fd); + + EXPECT_NE(ERROR, client_fd); + + if (client_fd != ERROR) { + client_fds.push_back(client_fd); + struct sockaddr_in addr = create_addr(); + + EXPECT_EQ(ERROR, connect(client_fd, (struct sockaddr *)&addr, sizeof(addr))); + } + + // destruct + for (std::vector::iterator itr = client_fds.begin(); itr != client_fds.end(); ++itr) { + close(*itr); + } + client_fds.clear(); +} + +/* helper funcs */ +static struct sockaddr_in create_addr() { + struct sockaddr_in addr = {}; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(SERVER_IP); + addr.sin_port = htons(std::strtol(SERVER_PORT, NULL, 10)); + return addr; +} + +static int create_nonblock_client_fd() { + int client_fd; + int result_fcntl; + + client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (client_fd == ERROR) { + return ERROR; + } + result_fcntl = fcntl(client_fd, F_SETFL, O_NONBLOCK); + if (result_fcntl == ERROR) { + close(client_fd); + return ERROR; + } + return client_fd; +} diff --git a/test/unit_test/socket/test_socket.cpp b/test/unit_test/socket/test_socket.cpp deleted file mode 100644 index f9dfd519..00000000 --- a/test/unit_test/socket/test_socket.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - - -int main() -{ - std::vector test_portset; - test_portset.push_back("4242"); - test_portset.push_back("2424"); - - std::vector::iterator it = test_portset.begin(); - - while (it != test_portset.end()) - { - - it++; - } -} \ No newline at end of file