/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2024 Brett Sheffield <bacs@librecast.net> */

/*
 * router port/socket tests
 */

#include "test.h"
#include <librecast_pvt.h>
#include <librecast/router.h>

#define SOCKS 2

int main(void)
{
	lc_ctx_t *lctx;
	lc_router_t *r1;
	lc_socket_t *sock[SOCKS + 1] = {0};
	const unsigned int ports = 2;

	test_name("lc_router_socket_add() / lc_router_socket_del()");

	/* create context and router */
	lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()")) return test_status;
	r1 = lc_router_new(lctx, ports, 0);
	if (!test_assert(r1 != NULL, "lc_router_new()")) goto err_ctx_free;
	test_assert(r1->ports == ports, "router ports set");

	/* create some sockets */
	for (int i = 0; i < SOCKS + 1; i++) {
		sock[i] = lc_socket_new(lctx);
		if (!sock[i]) goto err_socks_free;
	}
	/* plug sockets into router */
	for (int i = 0; i < SOCKS; i++) {
		test_assert(lc_router_socket_add(r1, sock[i]) == 0, "lc_router_socket_add()[%i]", i);
	}
	/* check sockets are mapped to ports */
	lc_socket_t **port = r1->port;
	for (int i = 0; i < SOCKS; i++, port++) {
		test_assert(*port == sock[i], "port %i connected", i);
		/* socket has back-reference to router */
		test_assert(sock[i]->router == r1, "socket connected to router");
	}
	/* plug extra socket into router, router will be resized
	 * (LC_ROUTER_FLAG_FIXED not set)*/
	test_assert(lc_router_socket_add(r1, sock[SOCKS]) == 0,
			"lc_router_socket_add() - extend router");
	test_assert(r1->ports == (ports << 1), "router ports extended");
	test_assert(r1->port[ports] == sock[SOCKS], "extra socket was plugged");

	/* unplug socket and re-test */
	int del = 1;
	test_assert(lc_router_socket_del(r1, sock[del]) == 0, "lc_router_socket_del()[%i]", del);
	test_assert(r1->port[del] == NULL, "port %i disconnected", del);
	test_assert(sock[del]->router == NULL, "socket connected to router");

	/* try to disconnect already-disconnected socket */
	errno = 0;
	test_assert(lc_router_socket_del(r1, sock[del]) == -1, "lc_router_socket_del()[%i]", del);
	test_assert(errno == ENOENT, "errno = ENOENT");

err_socks_free:
	for (int i = 0; i < SOCKS; i++) {
		if (sock[i]) lc_socket_close(sock[i]);
	}
err_ctx_free:
	lc_ctx_free(lctx);
	return test_status;
}
