HelenOS sources

root/uspace/srv/net/tcp/test/ucall.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PCUT_TEST
  2. PCUT_TEST
  3. PCUT_TEST
  4. PCUT_TEST
  5. PCUT_TEST
  6. test_cstate_change
  7. test_conns_establish
  8. test_conns_tear_down

/*
 * Copyright (c) 2017 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <errno.h>
#include <inet/endpoint.h>
#include <io/log.h>
#include <pcut/pcut.h>

#include "../conn.h"
#include "../rqueue.h"
#include "../ucall.h"

PCUT_INIT;

PCUT_TEST_SUITE(ucall);

static void test_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
static void test_conns_establish(tcp_conn_t **, tcp_conn_t **);
static void test_conns_tear_down(tcp_conn_t *, tcp_conn_t *);

static tcp_rqueue_cb_t test_rqueue_cb = {
        .seg_received = tcp_as_segment_arrived
};

static tcp_cb_t test_conn_cb = {
        .cstate_change = test_cstate_change
};

static tcp_conn_status_t cconn_status;
static tcp_conn_status_t sconn_status;

static FIBRIL_MUTEX_INITIALIZE(cst_lock);
static FIBRIL_CONDVAR_INITIALIZE(cst_cv);

PCUT_TEST_BEFORE
{
        errno_t rc;

        /* We will be calling functions that perform logging */
        rc = log_init("test-tcp");
        PCUT_ASSERT_ERRNO_VAL(EOK, rc);

        rc = tcp_conns_init();
        PCUT_ASSERT_ERRNO_VAL(EOK, rc);

        tcp_rqueue_init(&test_rqueue_cb);
        tcp_rqueue_fibril_start();

        /* Enable internal loopback */
        tcp_conn_lb = tcp_lb_segment;
}

PCUT_TEST_AFTER
{
        tcp_rqueue_fini();
        tcp_conns_fini();
}

/** Test creating a listening passive connection and then deleting it. */
PCUT_TEST(listen_delete)
{
        tcp_conn_t *conn;
        inet_ep2_t epp;
        tcp_error_t trc;
        tcp_conn_status_t cstatus;

        inet_ep2_init(&epp);
        inet_addr(&epp.local.addr, 127, 0, 0, 1);
        epp.local.port = inet_port_user_lo;

        conn = NULL;
        trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock, &conn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
        PCUT_ASSERT_NOT_NULL(conn);

        tcp_uc_status(conn, &cstatus);
        PCUT_ASSERT_INT_EQUALS(st_listen, cstatus.cstate);

        trc = tcp_uc_close(conn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
        tcp_uc_delete(conn);
}

/** Test trying to connect to endpoint that sends RST back */
PCUT_TEST(connect_rst)
{
        tcp_conn_t *conn;
        inet_ep2_t epp;
        tcp_error_t trc;

        inet_ep2_init(&epp);
        inet_addr(&epp.local.addr, 127, 0, 0, 1);
        inet_addr(&epp.remote.addr, 127, 0, 0, 1);
        epp.remote.port = inet_port_user_lo;

        conn = NULL;
        trc = tcp_uc_open(&epp, ap_active, 0, &conn);
        PCUT_ASSERT_INT_EQUALS(TCP_ERESET, trc);
        PCUT_ASSERT_NULL(conn);
}

/** Test establishing a connection */
PCUT_TEST(conn_establish)
{
        tcp_conn_t *cconn, *sconn;

        test_conns_establish(&cconn, &sconn);
        test_conns_tear_down(cconn, sconn);
}

/** Test establishing and then closing down a connection first on one side,
 * then on_the other.
 */
PCUT_TEST(conn_est_close_seq)
{
        tcp_conn_t *cconn, *sconn;
        tcp_error_t trc;

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: establish");
        /* Establish */
        test_conns_establish(&cconn, &sconn);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: close cconn");
        /* Close client side */
        trc = tcp_uc_close(cconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait cconn = fin-wait-2");
        /* Wait for cconn to go to Fin-Wait-2 */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(cconn, &cconn_status);
        while (cconn_status.cstate != st_fin_wait_2)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait sconn = close-wait");
        /* Wait for sconn to go to Close-Wait */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(sconn, &sconn_status);
        while (sconn_status.cstate != st_close_wait)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: close sconn");
        /* Close server side */
        trc = tcp_uc_close(sconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait cconn = time-wait");
        /* Wait for cconn to go to Time-Wait */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(cconn, &cconn_status);
        while (cconn_status.cstate != st_time_wait)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait sconn = closed");
        /* Wait for sconn to go to Closed */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(sconn, &sconn_status);
        while (sconn_status.cstate != st_closed) {
                log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: sconn.status == %d", sconn_status.cstate);
                fibril_condvar_wait(&cst_cv, &cst_lock);
        }
        fibril_mutex_unlock(&cst_lock);

        log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: tear down");
        /* Tear down */
        test_conns_tear_down(cconn, sconn);
}

/** Test establishing and then simultaneously closing down a connection. */
PCUT_TEST(conn_est_close_simult)
{
        tcp_conn_t *cconn, *sconn;
        tcp_error_t trc;

        /* Establish */
        test_conns_establish(&cconn, &sconn);

        /* Close both sides simultaneously */
        trc = tcp_uc_close(cconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
        trc = tcp_uc_close(sconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);

        /* Wait for cconn to go to Time-Wait */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(cconn, &cconn_status);
        while (cconn_status.cstate != st_time_wait)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        /*
         * Wait for sconn to go to Closed or Time-Wait. The connection
         * goes to Closed if we managed to call tcp_uc_close() before
         * sconn received FIN. Otherwise it goes to Time-Wait.
         *
         * XXX We may want to add delay to the loopback here to be
         * absolutely sure that we go to Closing -> Time-Wait.
         */
        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(sconn, &sconn_status);
        while (sconn_status.cstate != st_time_wait &&
            sconn_status.cstate != st_closed)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        /* Tear down */
        test_conns_tear_down(cconn, sconn);
}

static void test_cstate_change(tcp_conn_t *conn, void *arg,
    tcp_cstate_t old_state)
{
        tcp_conn_status_t *status = (tcp_conn_status_t *)arg;

        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(conn, status);
        fibril_mutex_unlock(&cst_lock);
        fibril_condvar_broadcast(&cst_cv);
}

/** Establish client-server connection */
static void test_conns_establish(tcp_conn_t **rcconn, tcp_conn_t **rsconn)
{
        tcp_conn_t *cconn, *sconn;
        inet_ep2_t cepp, sepp;
        tcp_conn_status_t cstatus;
        tcp_error_t trc;

        /* Client EPP */
        inet_ep2_init(&cepp);
        inet_addr(&cepp.local.addr, 127, 0, 0, 1);
        inet_addr(&cepp.remote.addr, 127, 0, 0, 1);
        cepp.remote.port = inet_port_user_lo;

        /* Server EPP */
        inet_ep2_init(&sepp);
        inet_addr(&sepp.local.addr, 127, 0, 0, 1);
        sepp.local.port = inet_port_user_lo;

        /* Server side of the connection */
        sconn = NULL;
        trc = tcp_uc_open(&sepp, ap_passive, tcp_open_nonblock, &sconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
        PCUT_ASSERT_NOT_NULL(sconn);

        tcp_uc_set_cb(sconn, &test_conn_cb, &sconn_status);
        PCUT_ASSERT_EQUALS(&sconn_status, tcp_uc_get_userptr(sconn));

        tcp_uc_status(sconn, &cstatus);
        PCUT_ASSERT_INT_EQUALS(st_listen, cstatus.cstate);

        /* Client side of the connection */

        cconn = NULL;
        trc = tcp_uc_open(&cepp, ap_active, 0, &cconn);
        PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
        PCUT_ASSERT_NOT_NULL(cconn);

        tcp_uc_set_cb(cconn, &test_conn_cb, &cconn_status);
        PCUT_ASSERT_EQUALS(&cconn_status, tcp_uc_get_userptr(cconn));

        /* The client side of the connection should be established by now */

        tcp_uc_status(cconn, &cstatus);
        PCUT_ASSERT_INT_EQUALS(st_established, cstatus.cstate);

        /* Need to wait for server side */

        fibril_mutex_lock(&cst_lock);
        tcp_uc_status(sconn, &sconn_status);
        while (sconn_status.cstate != st_established)
                fibril_condvar_wait(&cst_cv, &cst_lock);
        fibril_mutex_unlock(&cst_lock);

        *rcconn = cconn;
        *rsconn = sconn;
}

/* Tear down client-server connection. */
static void test_conns_tear_down(tcp_conn_t *cconn, tcp_conn_t *sconn)
{
        tcp_uc_abort(cconn);
        tcp_uc_delete(cconn);

        tcp_uc_abort(sconn);
        tcp_uc_delete(sconn);
}

PCUT_EXPORT(ucall);

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub