Newer
Older
libkc / modules / test / src / test_socket.c
#include <stdio.h>
#include <errno.h>

#include <kc.h>
#include <kc_ut.h>
#include <kc_assert.h>
#include <kc_memory.h>
#include <kc_threads.h>
#include <kc_socket.h>

#include "ut.h"

// プロトタイプ宣言
static void test_socket_new(void);
static void test_socket_dgram1(void);
static void test_socket_stream1(void);
static void test_socket_stream2(void);
static void test_socket_mcast1_ipv4(void);
static void test_socket_mcast1_ipv6(void);

// テスト用変数
/**
 * KcSocket 単体テストスイート
 */
void suite_socket(void)
{
    KcUt *ut = KcUt_get_instance();
    ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new);
    ut->add(ut, UT_TESTCASE, "socket dgram1", test_socket_dgram1);
    ut->add(ut, UT_TESTCASE, "socket stream1", test_socket_stream1);
    ut->add(ut, UT_TESTCASE, "socket stream2", test_socket_stream2);
    ut->add(ut, UT_TESTCASE, "socket mcast1 ipv4", test_socket_mcast1_ipv4);
    ut->add(ut, UT_TESTCASE, "socket mcast1 ipv6", test_socket_mcast1_ipv6);
}

typedef struct
{
    int type;
    int family;
    const char *addr;
    const char *service;
    const char *mcast_addr;
    const char *mcast_iface;
    int exp_recv_size;
    const char *exp_recv_data;
    const char *exp_src_addr;
    const char *exp_src_service;
} TestSocketInfo;

static int test_socket_thread_echo_server(void *arg)
{
    TestSocketInfo *info = (TestSocketInfo *)arg;
    KcSocket *server = KcSocket_new(info->type, info->family);
    assert_not_null(server);

    bool ret = server->bind(server, info->addr, info->service);
    assert_true(ret);

    // マルチキャスト
    if (info->mcast_addr != NULL)
    {
        server->join(server, info->mcast_addr, info->mcast_iface);
    }

    char recv_buff[16] = {'\0'};
    char src_addr[NI_MAXHOST];
    char src_service[NI_MAXSERV];

    // メッセージ受信
    int recv_size;
    KcSocket *connected_socket = NULL;
    if (info->type == SOCK_STREAM)
    { // TCP
        ret = server->listen(server, 1);
        assert_true(ret);

        connected_socket = server->accept(server);
        assert_not_null(connected_socket);

        const char *raddr = connected_socket->get_remote_addr(connected_socket);
        assert_not_null(raddr);
        snprintf(src_addr, NI_MAXHOST, "%s", raddr);
        int rport = connected_socket->get_remote_port(connected_socket);
        assert_true(rport != 0);
        snprintf(src_service, NI_MAXSERV, "%d", rport);

        recv_size = connected_socket->recv(connected_socket, recv_buff, sizeof(recv_buff), 0);
    }
    else
    { // UDP
        recv_size = server->recvfrom(
            server, recv_buff, sizeof(recv_buff), 0,
            src_addr, NI_MAXHOST, src_service, NI_MAXSERV);
        printf("recv [%s] (size=%d)\n", recv_buff, recv_size);
    }

    assert_equals(info->exp_recv_size, recv_size);
    assert_equals(info->exp_recv_data, recv_buff);

    if (info->exp_src_addr)
    {
        assert_equals(info->exp_src_addr, src_addr);
    }
    if (info->exp_src_service)
    {
        assert_equals(info->exp_src_service, src_service);
    }

    // 応答送信
    int write_size;
    if (info->type == SOCK_STREAM)
    { // TCP
        write_size = connected_socket->send(connected_socket, recv_buff, recv_size, 0);
        ret = connected_socket->close(connected_socket);
        assert_true(ret);
        KcSocket_delete(connected_socket);
    }
    else
    { // UDP
        printf("sendto: %s:%s\n", src_addr, src_service);
        printf("  data: %s (size=%d)\n", recv_buff, recv_size);
        write_size = server->sendto(server, recv_buff, recv_size, 0, src_addr, src_service);
        if (info->mcast_addr != NULL)
        {
            server->leave(server, info->mcast_addr, info->mcast_iface);
        }
    }
    assert_equals(info->exp_recv_size, write_size);

    KcSocket_delete(server);
    return 0;
}

/**
 * Socket 生成/破棄。
 *
 * @process KcSocket_new を実行する。。
 * @result KcSocket が生成されること。
 *
 * @process KcSocket_delete にて Socket を破棄する。
 * @result Socket が破棄されること。
 */
static void test_socket_new(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_DGRAM,
        .family = AF_UNSPEC,
        .addr = "127.0.0.1",
        .service = "5000",
        .mcast_addr = NULL,
        .mcast_iface = NULL,
        .exp_recv_size = 4,
        .exp_recv_data = "ABC",
        .exp_src_addr = NULL,
        .exp_src_service = NULL};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC);
    assert_not_null(client);

    // メッセージ送信
    int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service);
    assert_equals(info.exp_recv_size, send_size);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_DGRAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET, family);

    // 応答受信
    char buff[16];
    int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0);
    assert_equals(info.exp_recv_size, recv_size);
    assert_equals(info.exp_recv_data, buff);
    bool ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}

/**
 * UDP 送受信
 */
static void test_socket_dgram1(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_DGRAM,
        .family = AF_UNSPEC,
        .addr = "127.0.0.1",
        .service = "5000",
        .mcast_addr = NULL,
        .mcast_iface = NULL,
        .exp_recv_size = 5,
        .exp_recv_data = "DEFG",
        .exp_src_addr = "127.0.0.1",
        .exp_src_service = "6000"};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC);
    assert_not_null(client);

    bool can_bind = client->bind(client, info.exp_src_addr, info.exp_src_service);
    assert_true(can_bind);

    const char *local_addr = client->get_local_addr(client);
    assert_equals(info.exp_src_addr, local_addr);
    int port = client->get_local_port(client);
    assert_equals(atoi(info.exp_src_service), port);

    char buff[1024];
    client->print_info(client, buff, sizeof(buff));
    assert_equals("local : 127.0.0.1:6000\nremote: -:-\n", buff);

    int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service);
    assert_equals(info.exp_recv_size, send_size);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_DGRAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET, family);

    // 応答受信
    int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0);
    assert_equals(info.exp_recv_size, recv_size);
    assert_equals(info.exp_recv_data, buff);
    bool ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}

/**
 * TCP 送受信
 */
static void test_socket_stream1(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_STREAM,
        .family = AF_UNSPEC,
        .addr = "127.0.0.1",
        .service = "5000",
        .mcast_addr = NULL,
        .mcast_iface = NULL,
        .exp_recv_size = 4,
        .exp_recv_data = "XYZ",
        .exp_src_addr = NULL,
        .exp_src_service = NULL};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC);
    assert_not_null(client);

    bool ret = client->connect(client, info.addr, info.service, NULL, NULL);
    assert_true(ret);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_STREAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET, family);

    int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0);
    assert_equals(4, send_size);

    // 応答受信
    char buff[16];
    int recv_size = client->recv(client, buff, sizeof(buff), 0);
    assert_equals(info.exp_recv_size, recv_size);
    ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}

/**
 * TCP 送受信
 */
static void test_socket_stream2(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_STREAM,
        .family = AF_UNSPEC,
        .addr = "127.0.0.1",
        .service = "5000",
        .mcast_addr = NULL,
        .mcast_iface = NULL,
        .exp_recv_size = 4,
        .exp_recv_data = "XYZ",
        .exp_src_addr = "127.0.0.1",
        .exp_src_service = "6000"};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC);
    assert_not_null(client);

    bool ret = client->connect(client, info.addr, info.service, info.exp_src_addr, info.exp_src_service);
    assert_true(ret);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_STREAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET, family);

    const char *local_addr = client->get_local_addr(client);
    assert_equals(info.exp_src_addr, local_addr);
    int port = client->get_local_port(client);
    assert_equals(atoi(info.exp_src_service), port);

    char buff[1024];
    client->print_info(client, buff, sizeof(buff));
    assert_equals("local : 127.0.0.1:6000\nremote: 127.0.0.1:5000\n", buff);

    int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0);
    assert_equals(4, send_size);

    // 応答受信
    int recv_size = client->recv(client, buff, sizeof(buff), 0);
    assert_equals(info.exp_recv_size, recv_size);
    printf("buff=%s\n", buff);
    ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}

/**
 * マルチキャスト送受信
 */
static void test_socket_mcast1_ipv4(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_DGRAM,
        .family = AF_UNSPEC,
        .addr = "0.0.0.0",
        .service = "5000",
        .mcast_addr = "224.0.0.10",
        .mcast_iface = NULL,
        .exp_recv_size = 6,
        .exp_recv_data = "AAAAA",
        .exp_src_addr = NULL,
        .exp_src_service = NULL};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC);
    assert_not_null(client);

    client->set_ifname(client, "10.0.2.15", NULL);
    int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.mcast_addr, info.service);
    assert_equals(info.exp_recv_size, send_size);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_DGRAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET, family);

    // 応答受信
    char buff[16];
    int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0);
    assert_equals(info.exp_recv_size, recv_size);
    assert_equals((char *)info.exp_recv_data, buff);
    bool ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}

/**
 * マルチキャスト送受信
 */
static void test_socket_mcast1_ipv6(void)
{
    // 受信側
    KcThread *server = KcThread_new(test_socket_thread_echo_server);
    TestSocketInfo info = {
        .type = SOCK_DGRAM,
        .family = AF_UNSPEC,
        .addr = "::",
        .service = "5000",
        .mcast_addr = "ff02::1",
        .mcast_iface = NULL,
        .exp_recv_size = 6,
        .exp_recv_data = "AAAAA",
        .exp_src_addr = NULL,
        .exp_src_service = NULL};
    server->start(server, &info);

    KcThread_msleep(100, false);

    // 送信側
    KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC);
    assert_not_null(client);

    client->set_ifname(client, NULL, "fe80::a00:27ff:fea0:521%enp0s3");
    // client->set_ifname(client, "::1", NULL);
    int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.mcast_addr, info.service);
    assert_equals(info.exp_recv_size, send_size);

    // ソケット取得
    socket_t sock = client->get_socket(client);
    assert_true(sock != INVALID_SOCKET);

    // タイプ確認
    int type = client->get_type(client);
    assert_equals(SOCK_DGRAM, type);

    // ファミリー確認 (IPv4 [AF_INET])
    int family = client->get_family(client);
    assert_equals(AF_INET6, family);

    // 応答受信
    char buff[16];
    int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0);
    assert_equals(info.exp_recv_size, recv_size);
    assert_equals((char *)info.exp_recv_data, buff);
    bool ret = client->close(client);
    assert_true(ret);

    KcSocket_delete(client);

    server->join(server);
    KcThread_delete(server);
}