224 lines
6.8 KiB
C++
224 lines
6.8 KiB
C++
#include "utils.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
using namespace mooncake;
|
|
|
|
TEST(UtilsTest, ByteSizeToString) {
|
|
EXPECT_EQ(byte_size_to_string(999), "999 B");
|
|
EXPECT_EQ(byte_size_to_string(2048), "2.00 KB");
|
|
EXPECT_EQ(byte_size_to_string(5ULL * 1024 * 1024 + 1234), "5.00 MB");
|
|
EXPECT_EQ(byte_size_to_string(15ULL * 1024 * 1024 * 1024), "15.00 GB");
|
|
EXPECT_EQ(byte_size_to_string(0), "0 B");
|
|
EXPECT_EQ(byte_size_to_string(1), "1 B");
|
|
EXPECT_EQ(byte_size_to_string(1024), "1.00 KB");
|
|
EXPECT_EQ(byte_size_to_string(1024 * 1024), "1.00 MB");
|
|
EXPECT_EQ(byte_size_to_string(1024ULL * 1024 * 1024), "1.00 GB");
|
|
EXPECT_EQ(byte_size_to_string(1024ULL * 1024 * 1024 * 1024), "1.00 TB");
|
|
EXPECT_EQ(byte_size_to_string(15 * 1024 + 134), "15.13 KB");
|
|
EXPECT_EQ(byte_size_to_string(15 * 1024 * 1024 + 44048), "15.04 MB");
|
|
}
|
|
|
|
TEST(UtilsTest, StringToByteSize) {
|
|
auto parsed = try_string_to_byte_size("16 MB");
|
|
ASSERT_TRUE(parsed.has_value());
|
|
EXPECT_EQ(parsed.value(), 16ULL * 1024 * 1024);
|
|
|
|
parsed = try_string_to_byte_size("0");
|
|
ASSERT_TRUE(parsed.has_value());
|
|
EXPECT_EQ(parsed.value(), 0);
|
|
|
|
EXPECT_FALSE(try_string_to_byte_size("-5").has_value());
|
|
EXPECT_FALSE(try_string_to_byte_size("16XB").has_value());
|
|
EXPECT_EQ(string_to_byte_size("-5"), 0);
|
|
}
|
|
|
|
TEST(UtilsTest, StringToBool) {
|
|
EXPECT_EQ(string_to_bool("1"), true);
|
|
EXPECT_EQ(string_to_bool("true"), true);
|
|
EXPECT_EQ(string_to_bool("YES"), true);
|
|
EXPECT_EQ(string_to_bool(" on "), true);
|
|
EXPECT_EQ(string_to_bool("0"), false);
|
|
EXPECT_EQ(string_to_bool("false"), false);
|
|
EXPECT_EQ(string_to_bool("No"), false);
|
|
EXPECT_EQ(string_to_bool(" off "), false);
|
|
EXPECT_EQ(string_to_bool("maybe"), std::nullopt);
|
|
EXPECT_EQ(string_to_bool(""), std::nullopt);
|
|
}
|
|
|
|
TEST(UtilsTest, IsPortAvailable) {
|
|
// Find an available port
|
|
int test_port = -1;
|
|
for (int port = 50000; port < 50010; ++port) {
|
|
if (isPortAvailable(port)) {
|
|
test_port = port;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT_NE(test_port, -1) << "Could not find available port for testing";
|
|
|
|
// Initially the port should be available
|
|
EXPECT_TRUE(isPortAvailable(test_port));
|
|
|
|
// Bind to the port to make it unavailable
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
ASSERT_NE(sock, -1);
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_port = htons(test_port);
|
|
|
|
ASSERT_EQ(bind(sock, (struct sockaddr*)&addr, sizeof(addr)), 0);
|
|
ASSERT_EQ(listen(sock, 1), 0);
|
|
|
|
// Now the port should be unavailable
|
|
EXPECT_FALSE(isPortAvailable(test_port));
|
|
|
|
// Clean up
|
|
close(sock);
|
|
|
|
// Port should be available again
|
|
EXPECT_TRUE(isPortAvailable(test_port));
|
|
}
|
|
|
|
TEST(UtilsTest, IsPortAvailableWithBindingConflict) {
|
|
// Find an available port
|
|
int test_port = -1;
|
|
for (int port = 40000; port < 40010; ++port) {
|
|
if (isPortAvailable(port)) {
|
|
test_port = port;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT_NE(test_port, -1) << "Could not find available port for testing";
|
|
|
|
// Initially the port should be available
|
|
EXPECT_TRUE(isPortAvailable(test_port));
|
|
|
|
// Bind to the port to make it unavailable
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
ASSERT_NE(sock, -1);
|
|
|
|
int opt = 1;
|
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_port = htons(test_port);
|
|
|
|
ASSERT_EQ(bind(sock, (struct sockaddr*)&addr, sizeof(addr)), 0);
|
|
ASSERT_EQ(listen(sock, 1), 0);
|
|
|
|
// Now the port should be unavailable
|
|
EXPECT_FALSE(isPortAvailable(test_port));
|
|
|
|
// Clean up
|
|
close(sock);
|
|
|
|
// Port should be available again
|
|
EXPECT_TRUE(isPortAvailable(test_port));
|
|
}
|
|
|
|
TEST(UtilsTest, AutoPortBinderMultipleInstances) {
|
|
// Test that multiple binders get different ports
|
|
AutoPortBinder binder1;
|
|
AutoPortBinder binder2;
|
|
|
|
int port1 = binder1.getPort();
|
|
int port2 = binder2.getPort();
|
|
|
|
EXPECT_GT(port1, 0);
|
|
EXPECT_GT(port2, 0);
|
|
|
|
EXPECT_GE(port1, 12300); // Should be in default range
|
|
EXPECT_LE(port1, 14300);
|
|
EXPECT_GE(port2, 12300); // Should be in default range
|
|
EXPECT_LE(port2, 14300);
|
|
|
|
// If both successfully bound, they should have different ports
|
|
EXPECT_NE(port1, port2);
|
|
}
|
|
|
|
TEST(UtilsTest, SplitStringBasic) {
|
|
std::string input = "a, b ,c, d";
|
|
auto tokens = splitString(input, ',', true, false);
|
|
|
|
ASSERT_EQ(tokens.size(), 4);
|
|
EXPECT_EQ(tokens[0], "a");
|
|
EXPECT_EQ(tokens[1], "b");
|
|
EXPECT_EQ(tokens[2], "c");
|
|
EXPECT_EQ(tokens[3], "d");
|
|
}
|
|
|
|
TEST(UtilsTest, GetInterfaceIPv4AddressLoopback) {
|
|
auto address = GetInterfaceIPv4Address("lo");
|
|
ASSERT_TRUE(address.has_value()) << address.error();
|
|
EXPECT_EQ(address.value(), "127.0.0.1");
|
|
}
|
|
|
|
TEST(UtilsTest, GetInterfaceIPv4AddressMissingInterface) {
|
|
auto address = GetInterfaceIPv4Address("mooncake_missing_if");
|
|
ASSERT_FALSE(address.has_value());
|
|
EXPECT_NE(address.error().find("not found"), std::string::npos);
|
|
}
|
|
|
|
TEST(UtilsTest, AutoPortBinderRAII) {
|
|
// Test RAII behavior - port should be released when binder is destroyed
|
|
int port;
|
|
{
|
|
AutoPortBinder binder;
|
|
port = binder.getPort();
|
|
EXPECT_GT(port, 0); // Should successfully get a port
|
|
|
|
// Try to bind to the same port - should fail due to conflict
|
|
int test_socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
ASSERT_GE(test_socket, 0);
|
|
|
|
sockaddr_in addr = {};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_port = htons(port);
|
|
|
|
// This should fail because port is already bound by AutoPortBinder
|
|
int bind_result = bind(test_socket, (sockaddr*)&addr, sizeof(addr));
|
|
EXPECT_EQ(bind_result, -1); // Should fail
|
|
EXPECT_EQ(errno, EADDRINUSE); // Should be "address already in use"
|
|
|
|
close(test_socket);
|
|
|
|
} // AutoPortBinder destroyed here, port should be released
|
|
|
|
// Now try to bind to the same port again - should succeed
|
|
int test_socket2 = socket(AF_INET, SOCK_STREAM, 0);
|
|
ASSERT_GE(test_socket2, 0);
|
|
|
|
sockaddr_in addr2 = {};
|
|
addr2.sin_family = AF_INET;
|
|
addr2.sin_addr.s_addr = INADDR_ANY;
|
|
addr2.sin_port = htons(port);
|
|
|
|
// This should succeed because port was released
|
|
int bind_result2 = bind(test_socket2, (sockaddr*)&addr2, sizeof(addr2));
|
|
EXPECT_EQ(bind_result2, 0); // Should succeed
|
|
|
|
close(test_socket2);
|
|
}
|
|
|
|
TEST(UtilsTest, AutoPortBinderCustomRange) {
|
|
// Test custom port range
|
|
AutoPortBinder binder(50000, 50100);
|
|
int port = binder.getPort();
|
|
|
|
EXPECT_GE(port, 50000);
|
|
EXPECT_LE(port, 50100);
|
|
}
|