diff options
Diffstat (limited to 'hicn-light/src/hicn/test')
22 files changed, 4413 insertions, 0 deletions
diff --git a/hicn-light/src/hicn/test/CMakeLists.txt b/hicn-light/src/hicn/test/CMakeLists.txt new file mode 100644 index 000000000..395b6e333 --- /dev/null +++ b/hicn-light/src/hicn/test/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2021-2022 Cisco and/or its affiliates. + +include(BuildMacros) + +list(APPEND TESTS_SRC + test-configuration.cc + test-fib.cc + test-loop.cc + test-parser.cc + test-ctrl.cc + test-msgbuf_pool.cc + test-nexthops.cc + test-connection_table.cc + test-listener_table.cc + test-packet_cache.cc + test-strategy-load-balancing.cc + test-strategy-random.cc + test-strategy-replication.cc + test-strategy-best-path.cc + test-strategy-local-remote.cc + test-subscription.cc + test-local_prefixes.cc + test-probe_generator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ctrl/libhicnctrl/src/commands/command_listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ctrl/libhicnctrl/src/commands/command_route.c + main.cc +) + +build_executable(hicn_light_tests + NO_INSTALL + SOURCES ${TESTS_SRC} + LINK_LIBRARIES ${LIBHICN_LIGHT_STATIC} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} + DEPENDS gtest ${LIBHICNCTRL_STATIC} ${LIBHICN_LIGHT_SHARED} + COMPONENT ${HICN_LIGHT} + DEFINITIONS "${COMPILER_DEFINITIONS}" + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) + +add_test_internal(hicn_light_tests) diff --git a/hicn-light/src/hicn/test/main.cc b/hicn-light/src/hicn/test/main.cc new file mode 100644 index 000000000..49cc28f66 --- /dev/null +++ b/hicn-light/src/hicn/test/main.cc @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/hicn-light/src/hicn/test/test-configuration.cc b/hicn-light/src/hicn/test/test-configuration.cc new file mode 100644 index 000000000..2d3b5329f --- /dev/null +++ b/hicn-light/src/hicn/test/test-configuration.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include <gtest/gtest.h> + +extern "C" { +#include <hicn/config/configuration.h> +} + +static inline size_t CS_SIZE = 10; +static inline char CONFIG_FILE[] = "setup.conf"; +static inline int LOG_LEVEL = LOG_DEBUG; +static inline char LOG_FILE[] = "/dev/null"; +static inline uint16_t PORT = 1234; +static inline uint16_t CONF_PORT = 5678; +static inline bool IS_DAEMON_MODE = true; +static inline char PREFIX[] = "b001::/16"; +static inline char PREFIX_2[] = "c001::/16"; +static inline strategy_type_t STRATEGY_TYPE = STRATEGY_TYPE_BESTPATH; + +class ConfigurationTest : public ::testing::Test { + protected: + ConfigurationTest() { + config = configuration_create(); + log_conf.log_level = LOG_FATAL; + log_conf.log_file = NULL; + } + virtual ~ConfigurationTest() { configuration_free(config); } + + configuration_t *config; +}; + +TEST_F(ConfigurationTest, CreateConfiguration) { + // Check configuration creation + ASSERT_NE(config, nullptr); +} + +TEST_F(ConfigurationTest, SetGeneralParameters) { + configuration_set_cs_size(config, CS_SIZE); + size_t cs_size = configuration_get_cs_size(config); + EXPECT_EQ(cs_size, CS_SIZE); + + configuration_set_fn_config(config, CONFIG_FILE); + const char *config_file = configuration_get_fn_config(config); + EXPECT_EQ(config_file, CONFIG_FILE); + + configuration_set_port(config, PORT); + uint16_t port = configuration_get_port(config); + EXPECT_EQ(port, PORT); + + configuration_set_configuration_port(config, CONF_PORT); + uint16_t conf_port = configuration_get_configuration_port(config); + EXPECT_EQ(conf_port, CONF_PORT); + + configuration_set_daemon(config, IS_DAEMON_MODE); + bool is_daemon_mode = configuration_get_daemon(config); + EXPECT_EQ(is_daemon_mode, IS_DAEMON_MODE); +} + +TEST_F(ConfigurationTest, SetLogParameters) { + configuration_set_loglevel(config, LOG_LEVEL); + int log_level = configuration_get_loglevel(config); + EXPECT_EQ(log_level, LOG_LEVEL); + EXPECT_EQ(log_conf.log_level, LOG_LEVEL); + + configuration_set_logfile(config, LOG_FILE); + const char *log_file = configuration_get_logfile(config); + EXPECT_EQ(log_file, LOG_FILE); + int write_fd = configuration_get_logfile_fd(config); + EXPECT_NE(write_fd, -1); + configuration_flush_log(); +} + +TEST_F(ConfigurationTest, SetStrategyParameter) { + configuration_set_strategy(config, PREFIX, STRATEGY_TYPE); + strategy_type_t strategy_type = configuration_get_strategy(config, PREFIX); + EXPECT_EQ(strategy_type, STRATEGY_TYPE); + + // Check strategy for non-registered prefix + strategy_type = configuration_get_strategy(config, PREFIX_2); + EXPECT_EQ(strategy_type, STRATEGY_TYPE_UNDEFINED); +} diff --git a/hicn-light/src/hicn/test/test-connection_table.cc b/hicn-light/src/hicn/test/test-connection_table.cc new file mode 100644 index 000000000..6723d0ff1 --- /dev/null +++ b/hicn-light/src/hicn/test/test-connection_table.cc @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/connection_table.h> +#include <hicn/util/log.h> +#include <hicn/validation.h> +} + +#define CONNECTION_NAME "conn_name" +#define CONNECTION_NAME_2 "conn_name_2" + +class ConnectionTableTest : public ::testing::Test { + protected: + ConnectionTableTest() { + assert(is_symbolic_name(CONNECTION_NAME, SYMBOLIC_NAME_LEN)); + assert(is_symbolic_name(CONNECTION_NAME_2, SYMBOLIC_NAME_LEN)); + + log_conf.log_level = LOG_WARN; + + conn_table_ = connection_table_create(); + pair_ = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + } + virtual ~ConnectionTableTest() { connection_table_free(conn_table_); } + + connection_table_t *conn_table_; + connection_t *connection_; + address_pair_t pair_; +}; + +TEST_F(ConnectionTableTest, CreateTable) { + /* Check connection_table allocation */ + EXPECT_NE(conn_table_, nullptr); + + /* Check connection_table size */ + size_t conn_table_size = connection_table_len(conn_table_); + EXPECT_EQ(conn_table_size, (size_t)0); +} + +TEST_F(ConnectionTableTest, AddConnection) { + // Add connection to connection table + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + size_t conn_table_size = connection_table_len(conn_table_); + EXPECT_EQ(conn_table_size, (size_t)1); + EXPECT_NE(connection_, nullptr); + + // Get connection by name and by pair + khiter_t k_name = kh_get_ct_name(conn_table_->id_by_name, CONNECTION_NAME); + EXPECT_NE(k_name, kh_end(conn_table_->id_by_name)); + khiter_t k_pair = kh_get_ct_pair(conn_table_->id_by_pair, &pair_); + EXPECT_NE(k_pair, kh_end(conn_table_->id_by_pair)); +} + +TEST_F(ConnectionTableTest, GetConnection) { + // Add connection to connection table + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + size_t conn_table_size = connection_table_len(conn_table_); + EXPECT_EQ(conn_table_size, (size_t)1); + EXPECT_NE(connection_, nullptr); + + // Get connection by name + connection_t *connection_retrieved = + connection_table_get_by_name(conn_table_, CONNECTION_NAME); + ASSERT_NE(connection_retrieved, nullptr); + EXPECT_EQ(connection_retrieved, connection_); + + // Get connection by pair + connection_retrieved = connection_table_get_by_pair(conn_table_, &pair_); + ASSERT_NE(connection_retrieved, nullptr); + EXPECT_EQ(connection_retrieved, connection_); +} + +TEST_F(ConnectionTableTest, GetConnectionWithIdOutOfRange) { + connection_t *connection = _connection_table_get_by_id(conn_table_, ~0); + EXPECT_EQ(connection, nullptr); +} + +TEST_F(ConnectionTableTest, GetConnectionWithInvalidId) { + // First connection inserted has always id equal to 0 + int non_valid_id = 5; + + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + connection_t *connection_not_found = + connection_table_get_by_id(conn_table_, non_valid_id); + ASSERT_EQ(connection_not_found, nullptr); +} + +TEST_F(ConnectionTableTest, GetConnectionWithValidId) { + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + int id = connection_table_get_connection_id(conn_table_, connection_); + connection_t *connection_found = connection_table_get_by_id(conn_table_, id); + ASSERT_EQ(connection_found, connection_); +} + +TEST_F(ConnectionTableTest, GetConnectionIdFromValidName) { + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + int id = connection_table_get_id_by_name(conn_table_, CONNECTION_NAME); + ASSERT_TRUE(listener_id_is_valid(id)); +} + +TEST_F(ConnectionTableTest, GetConnectionIdFromInvalidName) { + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + int id = connection_table_get_id_by_name(conn_table_, CONNECTION_NAME_2); + ASSERT_FALSE(listener_id_is_valid(id)); +} + +TEST_F(ConnectionTableTest, RemoveConnection) { + // Add connection (connection name and pair must be set) + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + connection_->name = (char *)CONNECTION_NAME; + connection_->pair = pair_; + + // Remove connection + int id = connection_table_get_connection_id(conn_table_, connection_); + connection_table_remove_by_id(conn_table_, id); + + // Check connection table size + size_t conn_table_size = connection_table_len(conn_table_); + EXPECT_EQ(conn_table_size, (size_t)0); + + // Check that previous connection is not valid anymore + connection_t *connection_not_found = + connection_table_get_by_id(conn_table_, id); + EXPECT_EQ(connection_not_found, nullptr); + connection_not_found = + connection_table_get_by_name(conn_table_, CONNECTION_NAME); + EXPECT_EQ(connection_not_found, nullptr); + connection_not_found = connection_table_get_by_pair(conn_table_, &pair_); + EXPECT_EQ(connection_not_found, nullptr); +} + +TEST_F(ConnectionTableTest, PrintTable) { + // Set verbose log level + int old_log_level = log_conf.log_level; + log_conf.log_level = LOG_INFO; + + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + // Insert an additional connection + address_pair_t pair_2 = + address_pair_factory(_ADDRESS4_LOCALHOST(3), _ADDRESS4_LOCALHOST(4)); + connection_t *connection_2 = + connection_table_allocate(conn_table_, &pair_2, CONNECTION_NAME_2); + connection_2->type = FACE_TYPE_TCP; + + testing::internal::CaptureStdout(); + connection_table_print_by_pair(conn_table_); + std::string std_out = testing::internal::GetCapturedStdout(); + + ASSERT_NE(std_out, ""); + + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:1")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:2")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:3")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:4")); + + log_conf.log_level = old_log_level; // Restore old log level +} + +TEST_F(ConnectionTableTest, AddMultipleConnections) { + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + + // Insert an additional connection + address_pair_t pair_2 = + address_pair_factory(_ADDRESS4_LOCALHOST(3), _ADDRESS4_LOCALHOST(4)); + connection_t *connection_2 = + connection_table_allocate(conn_table_, &pair_2, CONNECTION_NAME_2); + connection_2->type = FACE_TYPE_TCP; + + // Check connection table size + size_t conn_table_size = connection_table_len(conn_table_); + EXPECT_EQ(conn_table_size, (size_t)2); + + connection_t *c1 = connection_table_get_by_name(conn_table_, CONNECTION_NAME); + ASSERT_NE(c1, nullptr); + connection_t *c2 = + connection_table_get_by_name(conn_table_, CONNECTION_NAME_2); + ASSERT_NE(c2, nullptr); + EXPECT_NE(c1, c2); +} + +TEST_F(ConnectionTableTest, Iterate) { + connection_ = connection_table_allocate(conn_table_, &pair_, CONNECTION_NAME); + connection_->type = FACE_TYPE_TCP; + connection_->pair = pair_; + + // Insert an additional connection + address_pair_t pair_2 = + address_pair_factory(_ADDRESS4_LOCALHOST(3), _ADDRESS4_LOCALHOST(4)); + connection_t *connection_2 = + connection_table_allocate(conn_table_, &pair_2, CONNECTION_NAME_2); + connection_2->type = FACE_TYPE_TCP; + connection_2->pair = pair_2; + + // Iterate over the connection table and count the connections + connection_t *c; + int count = 0; + connection_table_foreach(conn_table_, c, { count++; }); + EXPECT_EQ(count, 2); + + // Iterate over the connection table and check the connections + char local_addr_str[NI_MAXHOST], remote_addr_str[NI_MAXHOST]; + int local_port, remote_port; + testing::internal::CaptureStdout(); + connection_table_foreach(conn_table_, c, { + const address_pair_t *pair = connection_get_pair(c); + address_to_string(&pair->local, local_addr_str, &local_port); + address_to_string(&pair->remote, remote_addr_str, &remote_port); + + printf("%s:%d\t%s:%d\n", local_addr_str, local_port, remote_addr_str, + remote_port); + }); + + std::string std_out = testing::internal::GetCapturedStdout(); + ASSERT_NE(std_out, ""); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:1")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:2")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:3")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:4")); +} + +TEST_F(ConnectionTableTest, GenerateConnName) { + char conn_name[SYMBOLIC_NAME_LEN]; + int rc = connection_table_get_random_name(conn_table_, conn_name); + EXPECT_EQ(rc, 0); + + connection_ = connection_table_allocate(conn_table_, &pair_, conn_name); + connection_->type = FACE_TYPE_TCP; + connection_->pair = pair_; + + char conn_name2[SYMBOLIC_NAME_LEN]; + rc = connection_table_get_random_name(conn_table_, conn_name2); + EXPECT_EQ(rc, 0); + EXPECT_NE(strncmp(conn_name, conn_name2, SYMBOLIC_NAME_LEN), 0); +} + +TEST_F(ConnectionTableTest, GenerateConnNameExhaustion) { + char conn_name[SYMBOLIC_NAME_LEN]; + bool unable_to_allocate = false; + + // Force name exhaustion + int n_connections = 1 + USHRT_MAX; + for (int i = 0; i <= n_connections; i++) { + int rc = connection_table_get_random_name(conn_table_, conn_name); + if (rc < 0) { + unable_to_allocate = true; + break; + } + + address_pair_t pair = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(i)); + connection_t *conn = + connection_table_allocate(conn_table_, &pair, conn_name); + memset(conn, 0, sizeof(connection_t)); + conn->type = FACE_TYPE_TCP; + conn->pair = pair; + } + + EXPECT_TRUE(unable_to_allocate); +}
\ No newline at end of file diff --git a/hicn-light/src/hicn/test/test-ctrl.cc b/hicn-light/src/hicn/test/test-ctrl.cc new file mode 100644 index 000000000..f0d3e7c37 --- /dev/null +++ b/hicn-light/src/hicn/test/test-ctrl.cc @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +extern "C" { +#include <hicn/util/log.h> +#include <hicn/ctrl.h> +#include <hicn/ctrl/parse.h> +#include <hicn/ctrl/route.h> +#include <hicn/util/sstrncpy.h> +} + +class CtrlTest : public ::testing::Test { + protected: + CtrlTest() { + log_conf.log_level = LOG_INFO; + s_ = hc_sock_create_forwarder(FORWARDER_TYPE_HICNLIGHT); + } + virtual ~CtrlTest() { hc_sock_free(s_); } + + hc_sock_t *s_ = nullptr; + hc_command_t command_ = {}; +}; + +/** + * The parse() function is used to easily create the command. + * Here we test the serialization of the commands i.e. from command + * to message sent to the forwarder. + */ +#if 0 +TEST_F(CtrlTest, AddValidListener) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_TRUE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidProtocol) { + // Set invalid protocol (icmp) + std::string cmd = "add listener icmp udp0 10.0.0.1 9696 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidLocalPort) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + // Override with invalid port + command_.object.listener.local_port = 0; + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidLocalAddress) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + // Override with invalid family + command_.object.listener.family = -1; + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_EQ(success, false); +} + +TEST_F(CtrlTest, AddListenerEmptyLocalAddress) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + // Override with invalid address + command_.object.listener.local_addr = IP_ADDRESS_EMPTY; + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidSymbolicName) { + std::string cmd = "add listener udp 0udp 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidSymbolicName2) { + std::string cmd = "add listener udp udp! 10.0.0.1 9695 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddListenerInvalidInterfaceName) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth/0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_listener_create_conf(s_, &command_.object.listener); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddValidRoute) { + std::string cmd = "add route conn0 c001::/64 1"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + hc_result_t *result = hc_route_create_conf(s_, &command_.object.route); + bool success = hc_result_get_success(s_, result); + EXPECT_TRUE(success); +} + +TEST_F(CtrlTest, AddRouteInvalidLength) { + std::string cmd = "add route conn0 c001::/64 1"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + // Override with invalid prfix len + command_.object.route.len = MAX_IPV6_PREFIX_LEN + 1; + + hc_result_t *result = hc_route_create_conf(s_, &command_.object.route); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, AddRouteInvalidCost) { + std::string cmd = "add route conn0 c001::/64 1"; + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + + // Override with invalid cost + command_.object.route.cost = MAX_ROUTE_COST + 1; + + hc_result_t *result = hc_route_create_conf(s_, &command_.object.route); + bool success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); + + // Override with invalid cost + command_.object.route.cost = MIN_ROUTE_COST - 1; + + result = hc_route_create_conf(s_, &command_.object.route); + success = hc_result_get_success(s_, result); + EXPECT_FALSE(success); +} + +TEST_F(CtrlTest, RouteNameOrID) { + hc_route_t route = { + .face_id = (face_id_t)INVALID_FACE_ID, + .family = AF_INET6, + .remote_addr = IPV6_LOOPBACK, + .len = 64, + .cost = 1, + }; + + // At least one between name (symbolic or ID) and face_id + // should be set to make the route valid + + // Valid name (symbolic) + snprintf(route.name, SYMBOLIC_NAME_LEN, "%s", "test"); + EXPECT_EQ(hc_route_validate(&route), 0); + + // Valid name (ID) + snprintf(route.name, SYMBOLIC_NAME_LEN, "%s", "conn0"); + EXPECT_EQ(hc_route_validate(&route), 0); + + // Valid face_id + route.face_id = 1; + snprintf(route.name, SYMBOLIC_NAME_LEN, "%s", ""); + EXPECT_EQ(hc_route_validate(&route), 0); + + // Invalid name stating with number + // (face_id is only checked if empty name) + route.face_id = 1; + snprintf(route.name, SYMBOLIC_NAME_LEN, "%s", "1test"); + EXPECT_EQ(hc_route_validate(&route), -1); +} +#endif diff --git a/hicn-light/src/hicn/test/test-fib.cc b/hicn-light/src/hicn/test/test-fib.cc new file mode 100644 index 000000000..5068b4fa2 --- /dev/null +++ b/hicn-light/src/hicn/test/test-fib.cc @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> +#include <vector> + +extern "C" { +#define WITH_TESTS +#include <hicn/util/ip_address.h> +#include <hicn/config/configuration.h> +#include <hicn/core/forwarder.h> +#include <hicn/core/fib.h> +} + +/* + * TODO + * - test max_size + */ + +#define DEFAULT_SIZE 10 +#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a)))) + +class FibTest : public ::testing::Test { + protected: + FibTest() { fib = fib_create(NULL); } + virtual ~FibTest() { fib_free(fib); } + + configuration_t *configuration; + forwarder_t *forwarder; + fib_t *fib; +}; + +fib_entry_t *_fib_add_prefix(fib_t *fib, const hicn_prefix_t *prefix, + std::vector<uint32_t> &nexthops) { + fib_entry_t *entry = + fib_entry_create(prefix, STRATEGY_TYPE_UNDEFINED, NULL, NULL); + for (size_t i = 0; i < nexthops.size(); i++) + fib_entry_nexthops_add(entry, nexthops[i]); + fib_add(fib, entry); + return entry; +} + +int compare_str_prefix_to_prefix(char *p1, hicn_prefix_t *p2) { + char prefix_s[MAXSZ_IP_PREFIX]; + hicn_ip_prefix_t ipp; + hicn_prefix_get_ip_prefix(p2, &ipp); + hicn_ip_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, + (const hicn_ip_prefix_t *)&ipp); + return strcmp(prefix_s, p1); +} + +#define HICN_PREFIX(P, STR) \ + hicn_prefix_t P; \ + hicn_ip_prefix_t _##P; \ + EXPECT_EQ(hicn_ip_prefix_pton(STR, &_##P), 0); \ + EXPECT_EQ(hicn_prefix_create_from_ip_prefix(&_##P, &P), 0); + +/* TEST: Fib allocation and initialization */ +TEST_F(FibTest, FibAddOne) { + /* Empty fib should be valid */ + + HICN_PREFIX(pfx, "1122:3344:5566:7788:9900:aabb:ccdd:eeff/4"); + + const hicn_prefix_t *empty_prefix_array[] = {}; + bool empty_used_array[] = {}; + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, empty_prefix_array, empty_used_array)); + + const hicn_prefix_t *prefix_array[] = {&pfx}; + bool used_array[] = {true}; + + std::vector<uint32_t> empty_nexthop; + for (unsigned i = 0; i < ARRAY_SIZE(prefix_array); i++) { + if (!used_array[i]) continue; + _fib_add_prefix(fib, prefix_array[i], empty_nexthop); + } + + fib_dump(fib); + + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array, used_array)); + + /* Check that free indices and bitmaps are correctly updated */ +} + +TEST_F(FibTest, FibAddTwo) { + HICN_PREFIX(b001, "b001::/64"); + HICN_PREFIX(c001, "c001::/64"); + HICN_PREFIX(inner_8000_1, "8000::/1"); + + const hicn_prefix_t *prefix_array[] = {&b001, &inner_8000_1, &c001}; + bool used_array[] = {true, false, true}; + + std::vector<uint32_t> empty_nexthop; + _fib_add_prefix(fib, &b001, empty_nexthop); + _fib_add_prefix(fib, &c001, empty_nexthop); + + fib_dump(fib); + + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array, used_array)); +} + +TEST_F(FibTest, FibAddFive) { + HICN_PREFIX(b002, "b002::/64"); + HICN_PREFIX(b002_abcd_0, "b002::abcd:0:0:0/128"); + HICN_PREFIX(b002_2, "b002::2/128"); + HICN_PREFIX(b002_abcd_1, "b002::abcd:0:0:1/128"); + HICN_PREFIX(b002_3, "b002::3/128"); + HICN_PREFIX(inner_b002_2, "b002::2/127"); + HICN_PREFIX(inner_b002_abcd_0, "b002::abcd:0:0:0/127"); + + const hicn_prefix_t *prefix_array[] = { + &b002_2, &inner_b002_2, &b002_3, &b002, + &b002_abcd_0, &inner_b002_abcd_0, &b002_abcd_1}; + bool used_array[] = {true, false, true, true, true, false, true}; + + std::vector<uint32_t> empty_nexthop; + _fib_add_prefix(fib, &b002, empty_nexthop); + _fib_add_prefix(fib, &b002_abcd_0, empty_nexthop); + _fib_add_prefix(fib, &b002_2, empty_nexthop); + _fib_add_prefix(fib, &b002_abcd_1, empty_nexthop); + _fib_add_prefix(fib, &b002_3, empty_nexthop); + + fib_dump(fib); + + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array, used_array)); +} + +TEST_F(FibTest, FibAddRemove) { + HICN_PREFIX(b002_64, "b002::/64"); + HICN_PREFIX(b002_128, "b002::/128"); + + const hicn_prefix_t *prefix_array_1[] = {&b002_128}; + bool used_array_1[] = {true}; + const hicn_prefix_t *prefix_array_2[] = {}; + bool used_array_2[] = {}; + const hicn_prefix_t *prefix_array_3[] = {&b002_64}; + bool used_array_3[] = {true}; + + std::vector<uint32_t> empty_nexthop; + fib_entry_t *entry = _fib_add_prefix(fib, &b002_128, empty_nexthop); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array_1, used_array_1)); + + fib_remove_entry(fib, entry); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array_2, used_array_2)); + + entry = _fib_add_prefix(fib, &b002_64, empty_nexthop); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array_3, used_array_3)); +} + +TEST_F(FibTest, FibAddNested) { + HICN_PREFIX(b002_64, "b002::/64"); + HICN_PREFIX(b002_128, "b002::/128"); + + const hicn_prefix_t *prefix_array_1[] = {&b002_128}; + bool used_array_1[] = {true}; + const hicn_prefix_t *prefix_array_2[] = {&b002_128, &b002_64}; + bool used_array_2[] = {true, true}; + + std::vector<uint32_t> empty_nexthop; + _fib_add_prefix(fib, &b002_128, empty_nexthop); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array_1, used_array_1)); + + _fib_add_prefix(fib, &b002_64, empty_nexthop); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + EXPECT_TRUE(fib_check_preorder(fib, prefix_array_2, used_array_2)); +} + +TEST_F(FibTest, IRIStest) { + char p_0_s[] = "b001:0:0:3039::/64"; + char p_1_3_s[] = "b001::3039:0:1:2:0/128"; + HICN_PREFIX(p_0, p_0_s); + + HICN_PREFIX(p_1_2, "b001::3039:0:1:0:0/128"); + HICN_PREFIX(p_1_1, "b001::3039:0:1:0:100/128"); + HICN_PREFIX(p_1_3, p_1_3_s); + HICN_PREFIX(p_1_4, "b001::3039:0:1:0:102/128"); + + HICN_PREFIX(p_2_2, "b001::3039:0:2:0:0/128"); + HICN_PREFIX(p_2_1, "b001::3039:0:2:0:100/128"); + HICN_PREFIX(p_2_3, "b001::3039:0:2:2:0/128"); + HICN_PREFIX(p_2_4, "b001::3039:0:2:0:102/128"); + + HICN_PREFIX(to_match1, "b001::3039:0:1:0:101/128"); + HICN_PREFIX(to_match2, "b001:0:0:3039:ffff:ffff::/128"); + HICN_PREFIX(to_match3, "b001:1::/128"); + HICN_PREFIX(to_match4, "b001::3039:0:1:2:0/128"); + + std::vector<uint32_t> nexthop; + nexthop.push_back(2); // add nexthop 2 to the fib entry + /*** add ***/ + _fib_add_prefix(fib, &p_0, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + + _fib_add_prefix(fib, &p_1_1, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + + _fib_add_prefix(fib, &p_1_2, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + + _fib_add_prefix(fib, &p_1_3, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + + _fib_add_prefix(fib, &p_1_4, nexthop); + fib_dump(fib); + EXPECT_TRUE(fib_is_valid(fib)); + + /*** match ***/ + fib_entry_t *entry = fib_match_prefix(fib, &to_match1); + // the matching prefix should be p0 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_0_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } + + entry = fib_match_prefix(fib, &to_match2); + // the matching prefix should be p0 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_0_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } + + entry = fib_match_prefix(fib, &to_match3); + // we expect no match + EXPECT_FALSE(entry != NULL); + + entry = fib_match_prefix(fib, &to_match4); + // the matching prefix should be p_1_3 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_1_3_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } + + /*** remove ***/ + fib_remove(fib, &p_0, nexthop[0]); + EXPECT_TRUE(fib_is_valid(fib)); + fib_remove(fib, &p_1_1, nexthop[0]); + EXPECT_TRUE(fib_is_valid(fib)); + fib_remove(fib, &p_1_2, nexthop[0]); + EXPECT_TRUE(fib_is_valid(fib)); + fib_remove(fib, &p_1_3, nexthop[0]); + EXPECT_TRUE(fib_is_valid(fib)); + fib_remove(fib, &p_1_4, nexthop[0]); + EXPECT_TRUE(fib_is_valid(fib)); + fib_dump(fib); + + /*** match ***/ + entry = fib_match_prefix(fib, &to_match1); + // we expect no match + EXPECT_FALSE(entry != NULL); + + entry = fib_match_prefix(fib, &to_match2); + // we expect no match + EXPECT_FALSE(entry != NULL); + + entry = fib_match_prefix(fib, &to_match3); + // we expect no match + EXPECT_FALSE(entry != NULL); + + entry = fib_match_prefix(fib, &to_match4); + // we expect no match + EXPECT_FALSE(entry != NULL); + + // add again + _fib_add_prefix(fib, &p_0, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + _fib_add_prefix(fib, &p_2_1, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + _fib_add_prefix(fib, &p_2_2, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + _fib_add_prefix(fib, &p_2_3, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + _fib_add_prefix(fib, &p_2_4, nexthop); + EXPECT_TRUE(fib_is_valid(fib)); + fib_dump(fib); + + entry = fib_match_prefix(fib, &to_match1); + // the matching prefix should be p0 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_0_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } + + entry = fib_match_prefix(fib, &to_match2); + // the matching prefix should be p0 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_0_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } + + entry = fib_match_prefix(fib, &to_match3); + // we expect no match + EXPECT_FALSE(entry != NULL); + + entry = fib_match_prefix(fib, &to_match4); + // the matching prefix should be p0 + EXPECT_TRUE(entry != NULL); + if (entry) { + int ret = compare_str_prefix_to_prefix(p_0_s, &(entry->prefix)); + EXPECT_EQ(ret, 0); + } +} diff --git a/hicn-light/src/hicn/test/test-listener_table.cc b/hicn-light/src/hicn/test/test-listener_table.cc new file mode 100644 index 000000000..6bd2a6ab7 --- /dev/null +++ b/hicn-light/src/hicn/test/test-listener_table.cc @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/listener_table.h> +#include <hicn/util/log.h> +#include <hicn/validation.h> +} + +#define LISTENER_NAME "listener_name" +#define LISTENER_NAME_2 "listener_name_2" + +class ListenerTableTest : public ::testing::Test { + protected: + ListenerTableTest() { + assert(is_symbolic_name(LISTENER_NAME, SYMBOLIC_NAME_LEN)); + assert(is_symbolic_name(LISTENER_NAME_2, SYMBOLIC_NAME_LEN)); + + log_conf.log_level = LOG_INFO; + + listener_table_ = listener_table_create(); + key_ = listener_key_factory(_ADDRESS4_LOCALHOST(1), FACE_TYPE_UDP_LISTENER); + } + virtual ~ListenerTableTest() { listener_table_free(listener_table_); } + + listener_table_t *listener_table_; + listener_t *listener_; + listener_key_t key_; +}; + +TEST_F(ListenerTableTest, CreateTable) { + // Check listener_table allocation + EXPECT_NE(listener_table_, nullptr); + + // Check listener_table size + size_t listener_table_size = listener_table_len(listener_table_); + EXPECT_EQ(listener_table_size, (size_t)0); +} + +TEST_F(ListenerTableTest, AddListener) { + // Add listener to listener table + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + size_t listener_table_size = listener_table_len(listener_table_); + EXPECT_EQ(listener_table_size, (size_t)1); + EXPECT_NE(listener_, nullptr); + + // Get listener by name and by key + khiter_t k_name = kh_get_lt_name(listener_table_->id_by_name, LISTENER_NAME); + EXPECT_NE(k_name, kh_end(listener_table_->id_by_name)); + khiter_t k_key = kh_get_lt_key(listener_table_->id_by_key, &key_); + EXPECT_NE(k_key, kh_end(listener_table_->id_by_key)); +} + +TEST_F(ListenerTableTest, GetListener) { + // Add listener to listener table + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + size_t listener_table_size = listener_table_len(listener_table_); + EXPECT_EQ(listener_table_size, (size_t)1); + ASSERT_NE(listener_, nullptr); + + // Get listener by name + listener_t *listener_retrieved = + listener_table_get_by_name(listener_table_, LISTENER_NAME); + ASSERT_NE(listener_retrieved, nullptr); + EXPECT_EQ(listener_retrieved, listener_); + + // Get listener by key + listener_retrieved = listener_table_get_by_key(listener_table_, &key_); + ASSERT_NE(listener_retrieved, nullptr); + EXPECT_EQ(listener_retrieved, listener_); +} + +TEST_F(ListenerTableTest, GetListenerWithIdOutOfRange) { + listener_t *listener = _listener_table_get_by_id(listener_table_, ~0); + EXPECT_EQ(listener, nullptr); +} + +TEST_F(ListenerTableTest, GetListenerWithInvalidId) { + // First listener inserted has always id equal to 0 + int non_valid_id = 5; + + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + listener_t *listener_not_found = + listener_table_get_by_id(listener_table_, non_valid_id); + ASSERT_EQ(listener_not_found, nullptr); +} + +TEST_F(ListenerTableTest, GetListenerWithValidId) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + int id = listener_table_get_listener_id(listener_table_, listener_); + listener_t *listener_found = listener_table_get_by_id(listener_table_, id); + ASSERT_EQ(listener_found, listener_); +} + +TEST_F(ListenerTableTest, GetListenerIdFromValidName) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + int id = listener_table_get_id_by_name(listener_table_, LISTENER_NAME); + ASSERT_TRUE(listener_id_is_valid(id)); +} + +TEST_F(ListenerTableTest, GetListenerIdFromInvalidName) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + int id = listener_table_get_id_by_name(listener_table_, LISTENER_NAME_2); + ASSERT_FALSE(listener_id_is_valid(id)); +} + +TEST_F(ListenerTableTest, RemoveListener) { + // Add listener (listerner name and key must be set) + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + listener_->name = (char *)LISTENER_NAME; + listener_->key = key_; + + // Remove listener + int id = listener_table_get_listener_id(listener_table_, listener_); + listener_table_remove_by_id(listener_table_, id); + + // Check listener table size + size_t listener_table_size = listener_table_len(listener_table_); + EXPECT_EQ(listener_table_size, (size_t)0); + + // Check that previous listener is not valid anymore + listener_t *listener_not_found = + listener_table_get_by_id(listener_table_, id); + EXPECT_EQ(listener_not_found, nullptr); + listener_not_found = + listener_table_get_by_name(listener_table_, LISTENER_NAME); + EXPECT_EQ(listener_not_found, nullptr); + listener_not_found = listener_table_get_by_key(listener_table_, &key_); + EXPECT_EQ(listener_not_found, nullptr); +} + +TEST_F(ListenerTableTest, PrintTable) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + // Insert an additional listener + listener_key_t key_2 = + listener_key_factory(_ADDRESS4_LOCALHOST(2), FACE_TYPE_TCP_LISTENER); + listener_t *listener_2 = + listener_table_allocate(listener_table_, &key_2, LISTENER_NAME_2); + listener_2->type = FACE_TYPE_UDP_LISTENER; + + testing::internal::CaptureStdout(); + listener_table_print_by_key(listener_table_); + std::string std_out = testing::internal::GetCapturedStdout(); + + ASSERT_NE(std_out, ""); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:1")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:2")); +} + +TEST_F(ListenerTableTest, AddMultipleListeners) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + + // Insert an additional listener + listener_key_t key_2 = + listener_key_factory(_ADDRESS4_LOCALHOST(2), FACE_TYPE_TCP_LISTENER); + listener_t *listener_2 = + listener_table_allocate(listener_table_, &key_2, LISTENER_NAME_2); + listener_2->type = FACE_TYPE_UDP_LISTENER; + + // Check listener table size + size_t listener_table_size = listener_table_len(listener_table_); + EXPECT_EQ(listener_table_size, (size_t)2); + + listener_t *l1 = listener_table_get_by_name(listener_table_, LISTENER_NAME); + ASSERT_NE(l1, nullptr); + listener_t *l2 = listener_table_get_by_name(listener_table_, LISTENER_NAME_2); + ASSERT_NE(l2, nullptr); + EXPECT_NE(l1, l2); +} + +TEST_F(ListenerTableTest, Iterate) { + listener_ = listener_table_allocate(listener_table_, &key_, LISTENER_NAME); + listener_->type = FACE_TYPE_UDP_LISTENER; + listener_->key = key_; + + // Insert an additional listener + listener_key_t key_2 = + listener_key_factory(_ADDRESS4_LOCALHOST(2), FACE_TYPE_TCP_LISTENER); + listener_t *listener_2 = + listener_table_allocate(listener_table_, &key_2, LISTENER_NAME_2); + listener_2->type = FACE_TYPE_UDP_LISTENER; + listener_2->key = key_2; + + // Iterate over the listener table and count the listeners + int count = 0; + listener_table_foreach(listener_table_, l, { count++; }); + EXPECT_EQ(count, 2); + + // Iterate over the listener table and check the listeners + char addr_str[NI_MAXHOST]; + int port; + testing::internal::CaptureStdout(); + listener_table_foreach(listener_table_, l, { + address_to_string(&l->address, addr_str, &port); + printf("%s\t%s:%d\n", face_type_str(l->type), addr_str, port); + }); + + std::string std_out = testing::internal::GetCapturedStdout(); + ASSERT_NE(std_out, ""); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:1")); + EXPECT_THAT(std_out, testing::HasSubstr("127.0.0.1:2")); + EXPECT_THAT(std_out, testing::HasSubstr("UDP")); + EXPECT_THAT(std_out, testing::HasSubstr("TCP")); +} diff --git a/hicn-light/src/hicn/test/test-local_prefixes.cc b/hicn-light/src/hicn/test/test-local_prefixes.cc new file mode 100644 index 000000000..52b1c746e --- /dev/null +++ b/hicn-light/src/hicn/test/test-local_prefixes.cc @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <arpa/inet.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/strategies/local_prefixes.h> +#include <hicn/core/strategy.h> +} + +const char *name_str1 = "b001::0"; +const char *name_str2 = "b002::0"; +const char *name_str3 = "b003::0"; +const char *name_str4 = "b004::0"; +const char *name_str5 = "b005::0"; +const char *name_str6 = "b006::0"; +const char *name_str7 = "b007::0"; +const char *name_str8 = "b008::0"; +const char *name_str9 = "b009::0"; +const char *name_str10 = "b010::0"; +const char *name_str11 = "b011::0"; + +class LocalPrefixesTest : public ::testing::Test { + protected: + LocalPrefixesTest() {} + + virtual ~LocalPrefixesTest() {} +}; + +TEST_F(LocalPrefixesTest, LocalPrefixesAddName) { + int rc; + local_prefixes_t *lp = create_local_prefixes(); + EXPECT_FALSE(lp == nullptr); + + hicn_ip_address_t result = IP_ADDRESS_EMPTY; + hicn_ip_address_pton(name_str1, &result); + hicn_prefix_t name1; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name1); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str2, &result); + hicn_prefix_t name2; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name2); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str3, &result); + hicn_prefix_t name3; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name3); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str4, &result); + hicn_prefix_t name4; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name4); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str5, &result); + hicn_prefix_t name5; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name5); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str6, &result); + hicn_prefix_t name6; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name6); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str7, &result); + hicn_prefix_t name7; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name7); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str8, &result); + hicn_prefix_t name8; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name8); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str9, &result); + hicn_prefix_t name9; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name9); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str10, &result); + hicn_prefix_t name10; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name10); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str11, &result); + hicn_prefix_t name11; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name11); + EXPECT_EQ(rc, 0); + + local_prefixes_add_prefix(lp, &name1); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)1); + + local_prefixes_add_prefix(lp, &name1); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)1); + + local_prefixes_add_prefix(lp, &name2); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)2); + + local_prefixes_add_prefix(lp, &name2); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)2); + + local_prefixes_add_prefix(lp, &name3); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)3); + + local_prefixes_add_prefix(lp, &name4); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)4); + + local_prefixes_add_prefix(lp, &name5); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)5); + + local_prefixes_add_prefix(lp, &name6); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)6); + + local_prefixes_add_prefix(lp, &name7); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)7); + + local_prefixes_add_prefix(lp, &name8); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)8); + + local_prefixes_add_prefix(lp, &name9); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)9); + + local_prefixes_add_prefix(lp, &name10); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)10); + + local_prefixes_add_prefix(lp, &name11); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)10); + + free_local_prefixes(lp); +} + +TEST_F(LocalPrefixesTest, LocalPrefixesAddPrefixes) { + int rc; + local_prefixes_t *lp = create_local_prefixes(); + EXPECT_FALSE(lp == nullptr); + + hicn_ip_address_t result; + + local_prefixes_t *lp1 = create_local_prefixes(); + EXPECT_FALSE(lp1 == nullptr); + + hicn_ip_address_pton(name_str1, &result); + hicn_prefix_t name1; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name1); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str2, &result); + hicn_prefix_t name2; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name2); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str3, &result); + hicn_prefix_t name3; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name3); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str4, &result); + hicn_prefix_t name4; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name4); + EXPECT_EQ(rc, 0); + + local_prefixes_add_prefix(lp1, &name1); + local_prefixes_add_prefix(lp1, &name2); + local_prefixes_add_prefix(lp1, &name3); + local_prefixes_add_prefix(lp1, &name4); + + EXPECT_EQ(local_prefixes_get_len(lp1), (unsigned)4); + + local_prefixes_add_prefixes(lp, lp1); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)4); + + local_prefixes_add_prefixes(lp, lp1); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)4); + + local_prefixes_t *lp2 = create_local_prefixes(); + EXPECT_FALSE(lp2 == nullptr); + + hicn_ip_address_pton(name_str5, &result); + hicn_prefix_t name5; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name5); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str6, &result); + hicn_prefix_t name6; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name6); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str7, &result); + hicn_prefix_t name7; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name7); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str8, &result); + hicn_prefix_t name8; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name8); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str9, &result); + hicn_prefix_t name9; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name9); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str10, &result); + hicn_prefix_t name10; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name10); + EXPECT_EQ(rc, 0); + + hicn_ip_address_pton(name_str11, &result); + hicn_prefix_t name11; + rc = hicn_prefix_create_from_ip_address_len(&result, 128, &name11); + EXPECT_EQ(rc, 0); + + local_prefixes_add_prefix(lp2, &name5); + local_prefixes_add_prefix(lp2, &name6); + local_prefixes_add_prefix(lp2, &name7); + local_prefixes_add_prefix(lp2, &name8); + local_prefixes_add_prefix(lp2, &name9); + local_prefixes_add_prefix(lp2, &name10); + local_prefixes_add_prefix(lp2, &name11); + + EXPECT_EQ(local_prefixes_get_len(lp2), (unsigned)7); + + local_prefixes_add_prefixes(lp, lp2); + EXPECT_EQ(local_prefixes_get_len(lp), (unsigned)10); + + free_local_prefixes(lp); + free_local_prefixes(lp1); + free_local_prefixes(lp2); +} diff --git a/hicn-light/src/hicn/test/test-loop.cc b/hicn-light/src/hicn/test/test-loop.cc new file mode 100644 index 000000000..71635c929 --- /dev/null +++ b/hicn-light/src/hicn/test/test-loop.cc @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> +#include <fcntl.h> + +extern "C" { +#include <hicn/base/loop.h> +} + +class LoopTest : public ::testing::Test { + static constexpr uint16_t BUFFER_SIZE = 1024; + + protected: + LoopTest() + : server_port_(9191), + loop_(nullptr), + timer_tick_(2000), + connection_socket_(-1), + event_(nullptr), + timer_(nullptr) {} + + virtual ~LoopTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + static int onFirstTimerExpiration(void *owner, int fd, unsigned id, + void *arg) { + assert(id == 0); + std::cout << "This function should never be called" << std::endl; + EXPECT_TRUE(false); + return -1; + } + + static int onSecondTimerExpiration(void *owner, int fd, unsigned id, + void *arg) { + assert(id == 0); + std::cout << "First timer expired. Cancel second timer." << std::endl; + LoopTest *test = (LoopTest *)(arg); + loop_event_unregister(test->timer_); + return 0; + } + + static int onTimerExpiration(void *owner, int fd, unsigned id, void *arg) { + assert(id == 0); + // Create client socket + struct sockaddr_in addr; + int client_socket; + LoopTest *test = (LoopTest *)(arg); + + client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket == -1) { + perror("socket"); + return -1; + } + + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_family = AF_INET; + addr.sin_port = htons(test->server_port_); + + if (connect(client_socket, (struct sockaddr *)&addr, + sizeof(struct sockaddr)) == -1) { + perror("connect"); + return -1; + } + + if (send(client_socket, "Hello, world!\n", 14, 0) == -1) { + perror("send"); + return -1; + } + + close(client_socket); + loop_event_unregister(test->timer_); + + return 0; + } + + static int onNewConnection(void *owner, int fd, unsigned id, void *arg) { + assert(id == 0); + LoopTest *test = (LoopTest *)arg; + struct sockaddr_in addr; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_family = AF_INET; + + socklen_t addr_len = sizeof(struct sockaddr_in); + int ret; + + int client_fd = + accept(test->connection_socket_, (struct sockaddr *)(&addr), &addr_len); + if (client_fd == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { // NOSONAR + fprintf(stderr, "accept failed"); + } + + perror("accept"); + return -1; + } + + // Read whatever data available and close connection. + ret = read(client_fd, test->buffer, BUFFER_SIZE); + if (ret < 0) { + perror("read"); + return -1; + } + + test->buffer[ret] = '\0'; + std::cout << "Received: " << (char *)test->buffer << std::endl; + + close(client_fd); + loop_event_unregister(test->event_); + + return 0; + } + + void createTcpSocketServer() { + struct sockaddr_in addr; + int ret; + + /* Create local socket. */ + + connection_socket_ = socket(AF_INET, SOCK_STREAM, 0); + if (connection_socket_ == -1) { + perror("socket"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(server_port_); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(connection_socket_, (const struct sockaddr *)&addr, + sizeof(struct sockaddr_in)); + if (ret == -1) { + perror("bind"); + return; + } + + ret = listen(connection_socket_, 20); + if (ret == -1) { + perror("listen"); + return; + } + } + + uint16_t server_port_; + loop_t *loop_; + unsigned timer_tick_; + int connection_socket_; + event_t *event_; + event_t *timer_; + char buffer[BUFFER_SIZE]; +}; + +TEST_F(LoopTest, LoopCreateAndFree) { + loop_ = loop_create(); + EXPECT_TRUE(loop_ != NULL); + loop_free(loop_); + EXPECT_TRUE(loop_ != NULL); +} + +TEST_F(LoopTest, EventCreateAndFree) { + int ret; + + // Fake fd + int fd = socket(AF_INET, SOCK_STREAM, 0); + loop_ = loop_create(); + + ret = loop_fd_event_create(&event_, loop_, fd, nullptr, + &LoopTest::onNewConnection, 0, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(event_); + + // Register the event + ret = loop_fd_event_register(event_); + EXPECT_TRUE(ret >= 0); + + // Unregister the event + ret = loop_event_free(event_); + EXPECT_TRUE(ret >= 0); + + // close fd + ret = close(fd); + EXPECT_EQ(ret, 0); + + // Free event loop + loop_free(loop_); +} + +TEST_F(LoopTest, TimerCreateAndCancel) { + int ret; + event_t *timer2; + loop_ = loop_create(); + + ret = loop_timer_create(&timer_, loop_, nullptr, + &LoopTest::onFirstTimerExpiration, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer_); + + ret = loop_timer_create(&timer2, loop_, nullptr, + &LoopTest::onSecondTimerExpiration, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer2); + + // Register the 1st timer + ret = loop_timer_register(timer_, timer_tick_); + EXPECT_TRUE(ret >= 0); + + // Register the 2nd timer + ret = loop_timer_register(timer2, timer_tick_ / 2); + EXPECT_TRUE(ret >= 0); + + _loop_dispatch(loop_, 0); + + loop_undispatch(loop_); + + // Unregister the events + ret = loop_event_free(timer_); + ret += loop_event_free(timer2); + EXPECT_TRUE(ret >= 0); + + // Free event loop + loop_free(loop_); +} + +TEST_F(LoopTest, LoopDispatch) { + int ret; + + // Create new unix socket + createTcpSocketServer(); + loop_ = loop_create(); + + ret = loop_fd_event_create(&event_, loop_, connection_socket_, nullptr, + &LoopTest::onNewConnection, 0, this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(event_); + + ret = loop_fd_event_register(event_); + EXPECT_TRUE(ret >= 0); + + // Create timer. + ret = loop_timer_create(&timer_, loop_, nullptr, &LoopTest::onTimerExpiration, + this); + EXPECT_TRUE(ret >= 0); + EXPECT_TRUE(timer_); + + ret = loop_timer_register(timer_, timer_tick_); + EXPECT_TRUE(ret >= 0); + + // Start event dispatching + _loop_dispatch(loop_, 0); + + // Stop dispatching + loop_undispatch(loop_); + + // Free events + loop_event_free(timer_); + loop_event_free(event_); + + // Free event loop + loop_free(loop_); +} diff --git a/hicn-light/src/hicn/test/test-msgbuf_pool.cc b/hicn-light/src/hicn/test/test-msgbuf_pool.cc new file mode 100644 index 000000000..0a78a7a5d --- /dev/null +++ b/hicn-light/src/hicn/test/test-msgbuf_pool.cc @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/msgbuf_pool.h> +#include <hicn/util/pool.h> +} + +class MsgbufPoolTest : public ::testing::Test { + protected: + MsgbufPoolTest() { msgbuf_pool = msgbuf_pool_create(); } + virtual ~MsgbufPoolTest() { msgbuf_pool_free(msgbuf_pool); } + + msgbuf_pool_t *msgbuf_pool; +}; + +TEST_F(MsgbufPoolTest, Create) { + /* Check msgbuf_pool allocation */ + EXPECT_NE(msgbuf_pool, nullptr); + + /* Check msgbuf_pool size */ + size_t msgbuf_pool_size = pool_hdr(msgbuf_pool->buffers)->alloc_size; + EXPECT_EQ(msgbuf_pool_size, (size_t)PACKET_POOL_DEFAULT_INIT_SIZE); +} + +TEST_F(MsgbufPoolTest, GetMsgbuf) { + msgbuf_t *msgbuf = NULL; + + /* Get valid msgbuf from msgbuf_pool */ + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + + /* Check if the returned id is correct */ + off_t id = msgbuf_pool_get_id(msgbuf_pool, msgbuf); + EXPECT_EQ(id, msgbuf_id); + + /* Check if the returned msgbuf is correct */ + msgbuf_t *msgbuf_retrieved = msgbuf_pool_at(msgbuf_pool, id); + EXPECT_EQ(msgbuf_retrieved, msgbuf); +} + +TEST_F(MsgbufPoolTest, PutMsgbuf) { + /* Check that asking a msgbuf right after releasing another one + returns the same msgbuf */ + + msgbuf_t *msgbuf = NULL; + + off_t id1 = msgbuf_pool_get(msgbuf_pool, &msgbuf); + EXPECT_NE(msgbuf, nullptr); + + msgbuf_pool_put(msgbuf_pool, msgbuf); + + off_t id2 = msgbuf_pool_get(msgbuf_pool, &msgbuf); + EXPECT_NE(msgbuf, nullptr); + + EXPECT_EQ(id2, id1); +} + +TEST_F(MsgbufPoolTest, GetMultipleMsgbufs) { + const int NUM_MSG = 3; + msgbuf_t *msgbufs[NUM_MSG]; + + /* Check if successful allocation */ + int ret = msgbuf_pool_getn(msgbuf_pool, msgbufs, NUM_MSG); + EXPECT_EQ(ret, 0); + + /* Check if all msgbufs are valid */ + for (unsigned i = 0; i < NUM_MSG; i++) { + msgbuf_pool_get_id(msgbuf_pool, msgbufs[i]); + EXPECT_NE(msgbufs[i], nullptr) << "Invalid index: " << i; + } +} + +TEST_F(MsgbufPoolTest, AcquireMsgbuf) { + msgbuf_t *msgbuf = NULL; + + // Get msgbuf from msgbuf_pool + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + + // Acquire the msgbuf + msgbuf_pool_acquire(msgbuf); + EXPECT_NE(msgbuf, nullptr); + EXPECT_EQ(msgbuf->refs, 1u); + + msgbuf_pool_acquire(msgbuf); + EXPECT_NE(msgbuf, nullptr); + EXPECT_EQ(msgbuf->refs, 2u); +} + +TEST_F(MsgbufPoolTest, ReleaseMsgbuf) { + msgbuf_t *msgbuf = NULL; + + // Get msgbuf from msgbuf_pool + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + + // Acquire the msgbuf + msgbuf_pool_acquire(msgbuf); + EXPECT_EQ(msgbuf->refs, 1u); + + // Release the msgbuf + msgbuf_pool_release(msgbuf_pool, &msgbuf); + EXPECT_EQ(msgbuf, nullptr); +} + +TEST_F(MsgbufPoolTest, ReleaseNotAcquiredMsgbuf) { + msgbuf_t *msgbuf = NULL; + + // Get valid msgbuf from msgbuf_pool + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + +#ifndef NDEBUG + // Release the msgbuf even if it is not been acquired + ASSERT_DEATH( + { + msgbuf_pool_release(msgbuf_pool, &msgbuf); + ; + }, + ".*Assertion.*"); +#endif +} + +TEST_F(MsgbufPoolTest, MultipleAcquireAndReleaseMsgbuf) { + msgbuf_t *msgbuf = NULL; + + // Get msgbuf from msgbuf_pool + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + + // Acquire the msgbuf multiple times + msgbuf_pool_acquire(msgbuf); + msgbuf_pool_acquire(msgbuf); + EXPECT_EQ(msgbuf->refs, 2u); + + // Release the msgbuf + msgbuf_pool_release(msgbuf_pool, &msgbuf); + EXPECT_NE(msgbuf, nullptr); + EXPECT_EQ(msgbuf->refs, 1u); + msgbuf_pool_release(msgbuf_pool, &msgbuf); + EXPECT_EQ(msgbuf, nullptr); +} + +TEST_F(MsgbufPoolTest, CloneMsgbuf) { + msgbuf_t *msgbuf = NULL; + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + EXPECT_NE(msgbuf, nullptr); + EXPECT_NE(msgbuf_id_is_valid((unsigned long)msgbuf_id), 0); + + msgbuf_t *new_msgbuf; + off_t new_msg_id = msgbuf_pool_clone(msgbuf_pool, &new_msgbuf, msgbuf_id); + + EXPECT_EQ(new_msgbuf, msgbuf_pool_at(msgbuf_pool, new_msg_id)); + EXPECT_NE(new_msgbuf, msgbuf_pool_at(msgbuf_pool, msgbuf_id)); + EXPECT_NE(new_msgbuf, msgbuf); + EXPECT_TRUE(memcmp(msgbuf, new_msgbuf, sizeof(msgbuf_t)) == 0); +} diff --git a/hicn-light/src/hicn/test/test-nexthops.cc b/hicn-light/src/hicn/test/test-nexthops.cc new file mode 100644 index 000000000..9063298fd --- /dev/null +++ b/hicn-light/src/hicn/test/test-nexthops.cc @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/nexthops.h> +#include <hicn/core/strategy.h> +} + +#define NEXTHOP1 50 +#define NEXTHOP2 51 +#define NEXTHOP3 52 + +class NexthopsTest : public ::testing::Test { + protected: + NexthopsTest() {} + + virtual ~NexthopsTest() {} + + nexthops_t nexthops; +}; + +TEST_F(NexthopsTest, NexthopsAdd) { + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + + EXPECT_TRUE(nexthops_get_len(&nexthops) == 1); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); +} + +TEST_F(NexthopsTest, NexthopsRemove) { + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_remove(&nexthops, NEXTHOP2); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 2); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_remove(&nexthops, NEXTHOP3); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 1); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_remove(&nexthops, NEXTHOP3); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 1); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_remove(&nexthops, NEXTHOP1); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 0); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); +} + +TEST_F(NexthopsTest, NexthopsClear) { + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_clear(&nexthops); + + EXPECT_TRUE(nexthops_get_len(&nexthops) == 0); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); +} + +TEST_F(NexthopsTest, NexthopsGetOne) { + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + unsigned nexthop = nexthops_get_one(&nexthops); + + EXPECT_TRUE(nexthops_contains(&nexthops, nexthop)); +} + +TEST_F(NexthopsTest, NexthopsSelect) { + int ret; + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + ret = nexthops_select(&nexthops, 2); + + EXPECT_TRUE(ret == 0); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 1); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + ret = nexthops_select(&nexthops, 0); + + EXPECT_TRUE(ret == 0); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 1); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); + + nexthops_reset(&nexthops); + + EXPECT_TRUE(ret == 0); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + ret = nexthops_select(&nexthops, 4); + + EXPECT_TRUE(ret == -1); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + ret = nexthops_select(&nexthops, 3); + + EXPECT_TRUE(ret == -1); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 3); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); +} + +TEST_F(NexthopsTest, NexthopsDisable) { + int ret; + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + + ret = nexthops_disable(&nexthops, 0); + + EXPECT_TRUE(ret == 0); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 2); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + ret = nexthops_disable(&nexthops, 2); + + EXPECT_TRUE(ret == 0); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 1); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); + + ret = nexthops_disable(&nexthops, 3); + EXPECT_TRUE(ret == -1); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 3); + EXPECT_TRUE(nexthops_get_curlen(&nexthops) == 1); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP3)); +} + +TEST_F(NexthopsTest, NexthopsState) { + strategy_nexthop_state_t state; + nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP2); + nexthops_add(&nexthops, NEXTHOP3); + nexthops.state[0].load_balancer.pi = 100; + nexthops.state[1].load_balancer.pi = 200; + nexthops.state[2].load_balancer.pi = 300; + + state = nexthops_state(&nexthops, 0); + EXPECT_TRUE(state.load_balancer.pi == 100); + + state = nexthops_state(&nexthops, 1); + EXPECT_TRUE(state.load_balancer.pi == 200); + + state = nexthops_state(&nexthops, 2); + EXPECT_TRUE(state.load_balancer.pi == 300); + + nexthops_remove(&nexthops, NEXTHOP1); + EXPECT_TRUE(nexthops_get_len(&nexthops) == 2); + EXPECT_FALSE(nexthops_contains(&nexthops, NEXTHOP1)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP2)); + EXPECT_TRUE(nexthops_contains(&nexthops, NEXTHOP3)); + + state = nexthops_state(&nexthops, 0); + EXPECT_TRUE(state.load_balancer.pi == 300); + + state = nexthops_state(&nexthops, 1); + EXPECT_TRUE(state.load_balancer.pi == 200); +} + +TEST_F(NexthopsTest, NexthopsEqual) { + nexthops = NEXTHOPS_EMPTY; + nexthops_t nexthops_eq = NEXTHOPS_EMPTY; + nexthops_t nexthops_not_eq = NEXTHOPS_EMPTY; + + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP3); + nexthops_add(&nexthops_eq, NEXTHOP1); + nexthops_add(&nexthops_eq, NEXTHOP3); + nexthops_add(&nexthops_not_eq, NEXTHOP2); + + bool ret = nexthops_equal(&nexthops, &nexthops_eq); + EXPECT_TRUE(ret); + + ret = nexthops_equal(&nexthops, &nexthops_not_eq); + EXPECT_FALSE(ret); +} + +TEST_F(NexthopsTest, NexthopsCopy) { + nexthops = NEXTHOPS_EMPTY; + nexthops_t nexthops_eq = NEXTHOPS_EMPTY; + + nexthops_add(&nexthops, NEXTHOP1); + nexthops_add(&nexthops, NEXTHOP3); + nexthops_copy(&nexthops, &nexthops_eq); + + bool ret = nexthops_equal(&nexthops, &nexthops_eq); + EXPECT_TRUE(ret); +} diff --git a/hicn-light/src/hicn/test/test-packet_cache.cc b/hicn-light/src/hicn/test/test-packet_cache.cc new file mode 100644 index 000000000..37f4fe985 --- /dev/null +++ b/hicn-light/src/hicn/test/test-packet_cache.cc @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <optional> +#include <random> +#include <hicn/test/test-utils.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/packet_cache.h> +} + +static constexpr unsigned CS_SIZE = 100; +static constexpr unsigned CONN_ID = 0; +static constexpr unsigned CONN_ID_2 = 1; +static constexpr unsigned MSGBUF_ID = 0; +static constexpr unsigned MSGBUF_ID_2 = 1; +static constexpr unsigned MSGBUF_ID_3 = 2; +static constexpr unsigned FIVE_SECONDS = 5000; + +static constexpr int N_OPS = 50000; + +class PacketCacheTest : public ::testing::Test { + protected: + PacketCacheTest() { + pkt_cache = pkt_cache_create(CS_SIZE); + int rc = hicn_name_create_from_ip_address(IPV4_ANY, 0, &name); + EXPECT_EQ(rc, 0); + msgbuf_pool = msgbuf_pool_create(); + msgbuf = msgbuf_create(msgbuf_pool, CONN_ID, &name); + } + + virtual ~PacketCacheTest() { + msgbuf_pool_free(msgbuf_pool); + pkt_cache_free(pkt_cache); + } + + msgbuf_t *msgbuf_create(msgbuf_pool_t *msgbuf_pool, unsigned conn_id, + hicn_name_t *name, + std::optional<Ticks> lifetime = FIVE_SECONDS) { + msgbuf_t *msgbuf; + msgbuf_pool_get(msgbuf_pool, &msgbuf); + + msgbuf->connection_id = conn_id; + msgbuf_set_name(msgbuf, name); + + hicn_packet_set_format(msgbuf_get_pkbuf(msgbuf), + HICN_PACKET_FORMAT_IPV6_TCP); + hicn_packet_set_type(msgbuf_get_pkbuf(msgbuf), HICN_PACKET_TYPE_INTEREST); + + hicn_packet_buffer_t *pkbuf = msgbuf_get_pkbuf(msgbuf); + hicn_packet_set_buffer(pkbuf, msgbuf->packet, MTU, 0); + + int rc = hicn_packet_init_header(msgbuf_get_pkbuf(msgbuf), 0); + EXPECT_EQ(rc, 0); + + // Same as 'msgbuf_set_data_expiry_time', + // it would write in the same field + msgbuf_set_interest_lifetime(msgbuf, *lifetime); + + return msgbuf; + } + + hicn_name_t get_name_from_prefix(const char *prefix_str) { + hicn_ip_address_t prefix; + inet_pton(AF_INET6, prefix_str, (struct in6_addr *)&prefix); + + hicn_name_t name; + int rc = hicn_name_create_from_ip_address(prefix, 0, &name); + EXPECT_EQ(rc, 0); + + return name; + } + + pkt_cache_t *pkt_cache; + pkt_cache_entry_t *entry = nullptr; + msgbuf_pool_t *msgbuf_pool; + hicn_name_t name; + msgbuf_t *msgbuf; +}; + +TEST_F(PacketCacheTest, LowLevelOperations) { + kh_pkt_cache_prefix_t *prefix_to_suffixes = kh_init_pkt_cache_prefix(); + const hicn_name_prefix_t *prefix = hicn_name_get_prefix(&name); + _add_suffix(prefix_to_suffixes, prefix, 1, 11, pkt_cache->prefix_keys); + _add_suffix(prefix_to_suffixes, prefix, 2, 22, pkt_cache->prefix_keys); + + unsigned id = + _get_suffix(prefix_to_suffixes, prefix, 1, pkt_cache->prefix_keys); + EXPECT_EQ(id, 11UL); + + id = _get_suffix(prefix_to_suffixes, prefix, 2, pkt_cache->prefix_keys); + EXPECT_EQ(id, 22UL); + + id = _get_suffix(prefix_to_suffixes, prefix, 5, pkt_cache->prefix_keys); + EXPECT_EQ(id, HICN_INVALID_SUFFIX); + + _add_suffix(prefix_to_suffixes, prefix, 5, 55, pkt_cache->prefix_keys); + id = _get_suffix(prefix_to_suffixes, prefix, 5, pkt_cache->prefix_keys); + EXPECT_EQ(id, 55UL); + + _remove_suffix(prefix_to_suffixes, prefix, 2, pkt_cache->prefix_keys); + _add_suffix(prefix_to_suffixes, prefix, 2, 222, pkt_cache->prefix_keys); + id = _get_suffix(prefix_to_suffixes, prefix, 2, pkt_cache->prefix_keys); + EXPECT_EQ(id, 222UL); + + _prefix_map_free(prefix_to_suffixes); +} + +TEST_F(PacketCacheTest, CreatePacketCache) { + // Check packet cache allocation + EXPECT_NE(pkt_cache, nullptr); + pit_t *pit = pkt_cache_get_pit(pkt_cache); + ASSERT_NE(pit, nullptr); + cs_t *cs = pkt_cache_get_cs(pkt_cache); + ASSERT_NE(cs, nullptr); + + // Check sizes + ASSERT_EQ(pkt_cache_get_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); +} + +TEST_F(PacketCacheTest, AddPacketCacheEntry) { + // Add entry to the packet cache + entry = pkt_cache_allocate(pkt_cache); + EXPECT_NE(entry, nullptr); + entry->name = name; + ASSERT_EQ(pkt_cache_get_size(pkt_cache), 1u); + pkt_cache_add_to_index(pkt_cache, entry); + + // Get entry by name + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_lookup(pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, + true); + EXPECT_NE(lookup_result, PKT_CACHE_LU_NONE); +} + +TEST_F(PacketCacheTest, GetCS) { + cs_t *cs = pkt_cache_get_cs(pkt_cache); + ASSERT_NE(cs, nullptr); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + EXPECT_EQ(cs->type, CS_TYPE_LRU); + EXPECT_EQ(cs->num_entries, 0); + EXPECT_EQ(cs->lru.head, (off_t)INVALID_ENTRY_ID); + EXPECT_EQ(cs->lru.tail, (off_t)INVALID_ENTRY_ID); +} + +TEST_F(PacketCacheTest, GetPIT) { + pit_t *pit = pkt_cache_get_pit(pkt_cache); + ASSERT_NE(pit, nullptr); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); +} + +TEST_F(PacketCacheTest, LookupEmpty) { + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *entry = pkt_cache_lookup(pkt_cache, &name, msgbuf_pool, + &lookup_result, &entry_id, true); + + EXPECT_EQ(lookup_result, PKT_CACHE_LU_NONE); + EXPECT_EQ(entry, nullptr); +} + +TEST_F(PacketCacheTest, AddEntryAndLookup) { + // Add entry to the packet cache + entry = pkt_cache_allocate(pkt_cache); + entry->name = name; + entry->entry_type = PKT_CACHE_PIT_TYPE; + ASSERT_NE(entry, nullptr); + pkt_cache_add_to_index(pkt_cache, entry); + + // Perform lookup + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + + EXPECT_TRUE(lookup_result == PKT_CACHE_LU_INTEREST_NOT_EXPIRED || + lookup_result == PKT_CACHE_LU_INTEREST_EXPIRED); + EXPECT_NE(lu_entry, nullptr); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, AddToPIT) { + // Check if entry properly created + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_PIT_TYPE); + EXPECT_TRUE(pit_entry_ingress_contains(&entry->u.pit_entry, CONN_ID)); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, AddToCS) { + // Check if entry properly created + pkt_cache_entry_t *entry = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + cs_entry_t *cs_entry = &entry->u.cs_entry; + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_CS_TYPE); + EXPECT_EQ(cs_entry->msgbuf_id, MSGBUF_ID); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 1u); + + // Check if CS properly updated + cs_t *cs = pkt_cache_get_cs(pkt_cache); + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + EXPECT_EQ(cs->num_entries, 1); + EXPECT_EQ(cs->lru.head, entry_id); + EXPECT_EQ(cs->lru.tail, entry_id); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_DATA_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, PitToCS) { + // Prepare PIT entry + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if entry properly updated + pkt_cache_pit_to_cs(pkt_cache, entry, msgbuf_pool, msgbuf, MSGBUF_ID, + entry_id); + cs_entry_t *cs_entry = &entry->u.cs_entry; + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_CS_TYPE); + EXPECT_EQ(cs_entry->msgbuf_id, MSGBUF_ID); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 1u); + + // Check if CS properly updated + cs_t *cs = pkt_cache_get_cs(pkt_cache); + entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + EXPECT_EQ(cs->num_entries, 1); + EXPECT_EQ(cs->lru.head, entry_id); + EXPECT_EQ(cs->lru.tail, entry_id); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_DATA_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, CsToPIT) { + // Prepare CS entry + pkt_cache_entry_t *entry = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 1u); + + // Check if entry properly updated + pkt_cache_cs_to_pit(pkt_cache, entry, msgbuf_pool, msgbuf, MSGBUF_ID, + entry_id); + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_PIT_TYPE); + EXPECT_TRUE(pit_entry_ingress_contains(&entry->u.pit_entry, CONN_ID)); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, UpdateInPIT) { + // Prepare PIT entry + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + + hicn_name_t new_name; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &new_name); + EXPECT_EQ(rc, 0); + msgbuf_t *new_msgbuf = msgbuf_create(msgbuf_pool, CONN_ID_2, &new_name); + + // Check if entry properly updated + pkt_cache_update_pit(pkt_cache, entry, new_msgbuf); + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_PIT_TYPE); + EXPECT_EQ(pit_entry_ingress_contains(&entry->u.pit_entry, CONN_ID_2), true); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, UpdateInCS) { + // Prepare CS entry + pkt_cache_entry_t *entry = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + + hicn_name_t new_name; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &new_name); + EXPECT_EQ(rc, 0); + msgbuf_t *new_msgbuf = msgbuf_create(msgbuf_pool, CONN_ID_2, &new_name); + + // Check if entry properly updated + pkt_cache_update_cs(pkt_cache, msgbuf_pool, entry, new_msgbuf, MSGBUF_ID_2); + cs_entry_t *cs_entry = &entry->u.cs_entry; + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_CS_TYPE); + EXPECT_EQ(cs_entry->msgbuf_id, MSGBUF_ID_2); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 1u); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_DATA_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, RemoveFromPIT) { + // Prepare PIT entry + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + pkt_cache_pit_remove_entry(pkt_cache, entry); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_NONE); + EXPECT_EQ(lu_entry, nullptr); +} + +TEST_F(PacketCacheTest, RemoveFromCS) { + // Prepare CS entry + pkt_cache_entry_t *entry = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 1u); + + pkt_cache_cs_remove_entry(pkt_cache, entry, msgbuf_pool, false); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + + // Check if CS properly updated + cs_t *cs = pkt_cache_get_cs(pkt_cache); + EXPECT_EQ(cs->num_entries, 0); + EXPECT_EQ(cs->lru.head, (off_t)INVALID_ENTRY_ID); + EXPECT_EQ(cs->lru.tail, (off_t)INVALID_ENTRY_ID); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_NONE); + EXPECT_EQ(lu_entry, nullptr); +} + +TEST_F(PacketCacheTest, AddTwoEntriesToCS) { + // Prepare another msgbuf + hicn_name_t new_name; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &new_name); + EXPECT_EQ(rc, 0); + msgbuf_t *new_msgbuf = msgbuf_create(msgbuf_pool, CONN_ID_2, &new_name); + + pkt_cache_entry_t *entry_1 = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + pkt_cache_entry_t *entry_2 = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, new_msgbuf, MSGBUF_ID_2); + off_t entry_id_1 = pkt_cache_get_entry_id(pkt_cache, entry_1); + off_t entry_id_2 = pkt_cache_get_entry_id(pkt_cache, entry_2); + + // Check if the CS and LRU cache are properly updated + cs_t *cs = pkt_cache_get_cs(pkt_cache); + EXPECT_EQ(cs->num_entries, 2); + EXPECT_EQ(cs->lru.head, entry_id_2); + EXPECT_EQ(cs->lru.tail, entry_id_1); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 0u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 2u); +} + +TEST_F(PacketCacheTest, AggregateInPIT) { + // Prepare another msgbuf + hicn_name_t new_name; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &new_name); + EXPECT_EQ(rc, 0); + msgbuf_t *new_msgbuf = msgbuf_create(msgbuf_pool, CONN_ID_2, &new_name); + + // Check if entry properly created (use sleep to get an updated ts) + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + Ticks old_lifetime = entry->expire_ts; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool is_aggregated = + pkt_cache_try_aggregate_in_pit(pkt_cache, entry, new_msgbuf, &name); + Ticks new_lifetime = entry->expire_ts; + + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_PIT_TYPE); + EXPECT_GT(new_lifetime, old_lifetime); + ASSERT_EQ(is_aggregated, true); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, RetransmissionInPIT) { + // Prepare another msgbuf (using same connection ID) + hicn_name_t new_name; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &new_name); + EXPECT_EQ(rc, 0); + msgbuf_t *new_msgbuf = msgbuf_create(msgbuf_pool, CONN_ID, &new_name); + + // Check if entry properly created (use sleep to get an updated ts) + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + Ticks old_lifetime = entry->expire_ts; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool is_aggregated = + pkt_cache_try_aggregate_in_pit(pkt_cache, entry, new_msgbuf, &name); + Ticks new_lifetime = entry->expire_ts; + + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->entry_type, PKT_CACHE_PIT_TYPE); + EXPECT_GT(new_lifetime, old_lifetime); + ASSERT_EQ(is_aggregated, false); + + // Check if hashtable correctly updated + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_entry_t *lu_entry = pkt_cache_lookup( + pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_NOT_EXPIRED); + EXPECT_EQ(lu_entry, entry); +} + +TEST_F(PacketCacheTest, LookupExpiredInterest) { + // Prepare msgbuf with 0 as interest lifetime + msgbuf_t *msgbuf = msgbuf_create(msgbuf_pool, CONN_ID, &name, 0); + + // Add to PIT + pkt_cache_entry_t *entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, &name); + ASSERT_NE(entry, nullptr); + + // Wait to make the interest expire + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_lookup(pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, + true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_INTEREST_EXPIRED); +} + +TEST_F(PacketCacheTest, LookupExpiredData) { + // Prepare msgbuf with 0 as data expiry time + msgbuf_t *msgbuf = msgbuf_create(msgbuf_pool, CONN_ID, &name, 0); + + // Add to CS + pkt_cache_entry_t *entry = + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + ASSERT_NE(entry, nullptr); + + // Wait to make the interest expire + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + pkt_cache_lookup_t lookup_result; + off_t entry_id; + pkt_cache_lookup(pkt_cache, &name, msgbuf_pool, &lookup_result, &entry_id, + true); + EXPECT_EQ(lookup_result, PKT_CACHE_LU_DATA_EXPIRED); +} + +TEST_F(PacketCacheTest, GetStaleEntries) { + // Add to CS a msgbuf with immediate expiration (i.e. stale) + msgbuf_t *msgbuf = msgbuf_create(msgbuf_pool, CONN_ID, &name, 0); + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + + // Add to CS another msgbuf with immediate expiration (i.e. stale) + hicn_name_t name_2; + int rc = hicn_name_create_from_ip_address(IPV4_LOOPBACK, 0, &name_2); + EXPECT_EQ(rc, 0); + msgbuf_t *msgbuf_2 = msgbuf_create(msgbuf_pool, CONN_ID, &name_2, 0); + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf_2, MSGBUF_ID_2); + + // Add to CS a msgbuf with 5-seconds expiration (i.e. not stale) + hicn_name_t name_3; + rc = hicn_name_create_from_ip_address(IPV6_LOOPBACK, 0, &name_3); + EXPECT_EQ(rc, 0); + msgbuf_t *msgbuf_3 = + msgbuf_create(msgbuf_pool, CONN_ID, &name_3, FIVE_SECONDS); + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf_3, MSGBUF_ID_3); + + size_t num_stale_entries = pkt_cache_get_num_cs_stale_entries(pkt_cache); + EXPECT_EQ(num_stale_entries, 2u); +} + +TEST_F(PacketCacheTest, GetMultipleStaleEntries) { + hicn_ip_address_t addr; + char name[30]; + const int NUM_STALES = 10; + int rc; + + // Add to CS multiple msgbufs with immediate expiration (i.e. 0 seconds), + // resulting in stale entries + for (int i = 0; i < NUM_STALES; i++) { + snprintf(name, 30, "b001::%d", i); + inet_pton(AF_INET6, name, (struct in6_addr *)&addr); + hicn_name_t name; + rc = hicn_name_create_from_ip_address(addr, 0, &name); + EXPECT_EQ(rc, 0); + msgbuf_t *msgbuf = msgbuf_create(msgbuf_pool, i, &name, 0); + + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, i); + } + + // Add to CS multiple msgbufs with 5-seconds expiration, + // resulting in non-stale entries + for (int i = NUM_STALES; i < 15; i++) { + snprintf(name, 30, "b001::%d", i); + inet_pton(AF_INET6, name, (struct in6_addr *)&addr); + hicn_name_t name; + rc = hicn_name_create_from_ip_address(addr, 0, &name); + EXPECT_EQ(rc, 0); + msgbuf_t *msgbuf = msgbuf_create(msgbuf_pool, i, &name, FIVE_SECONDS); + + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, i); + } + + size_t num_stale_entries = pkt_cache_get_num_cs_stale_entries(pkt_cache); + EXPECT_EQ(num_stale_entries, (size_t)NUM_STALES); +} + +TEST_F(PacketCacheTest, PerformanceDoubleLookup) { + hicn_name_t tmp = get_name_from_prefix("b001::0"); + + auto elapsed_time_double = get_execution_time([&]() { + kh_pkt_cache_prefix_t *prefix_to_suffixes = kh_init_pkt_cache_prefix(); + + // Add to hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seq); + _add_suffix(prefix_to_suffixes, hicn_name_get_prefix(&tmp), + hicn_name_get_suffix(&tmp), hicn_name_get_suffix(&tmp), + pkt_cache->prefix_keys); + } + + // Read from hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seq); + _get_suffix(prefix_to_suffixes, hicn_name_get_prefix(&tmp), seq, + pkt_cache->prefix_keys); + } + + _prefix_map_free(prefix_to_suffixes); + }); + std::cout << "Double lookup: " << elapsed_time_double << " ms\n"; +} + +TEST_F(PacketCacheTest, PerformanceCachedLookup) { + hicn_name_t tmp = get_name_from_prefix("b001::0"); + + auto elapsed_time_single = get_execution_time([&]() { + kh_pkt_cache_prefix_t *prefix_to_suffixes = kh_init_pkt_cache_prefix(); + kh_pkt_cache_suffix_t *suffixes = + _get_suffixes(prefix_to_suffixes, hicn_name_get_prefix(&tmp), true, + pkt_cache->prefix_keys); + + // Add to hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seq); + __add_suffix(suffixes, hicn_name_get_suffix(&tmp), + hicn_name_get_suffix(&tmp)); + } + + // Read from hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seq); + __get_suffix(suffixes, hicn_name_get_suffix(&tmp)); + } + + _prefix_map_free(prefix_to_suffixes); + }); + std::cout << "Cached lookup: " << elapsed_time_single << " ms\n"; +} + +TEST_F(PacketCacheTest, PerformanceCachedLookupRandom) { + hicn_name_t tmp = get_name_from_prefix("b001::0"); + + // Prepare random sequence numbers + std::random_device rd; + std::mt19937 gen(rd()); + uint32_t seqs[N_OPS]; + for (int seq = 0; seq < N_OPS; seq++) seqs[seq] = seq; + std::shuffle(std::begin(seqs), std::end(seqs), gen); + + auto elapsed_time_single_rand = get_execution_time([&]() { + kh_pkt_cache_prefix_t *prefix_to_suffixes = kh_init_pkt_cache_prefix(); + kh_pkt_cache_suffix_t *suffixes = + _get_suffixes(prefix_to_suffixes, hicn_name_get_prefix(&tmp), true, + pkt_cache->prefix_keys); + + // Add to hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seqs[seq]); + __add_suffix(suffixes, hicn_name_get_suffix(&tmp), + hicn_name_get_suffix(&tmp)); + } + + // Read from hash table + for (int seq = 0; seq < N_OPS; seq++) { + hicn_name_set_suffix(&tmp, seqs[seq]); + __get_suffix(suffixes, hicn_name_get_suffix(&tmp)); + } + + _prefix_map_free(prefix_to_suffixes); + }); + std::cout << "Cached lookup (rand): " << elapsed_time_single_rand << " ms\n"; +} + +TEST_F(PacketCacheTest, Clear) { + hicn_name_t tmp_name1, tmp_name2; + cs_t *cs = pkt_cache_get_cs(pkt_cache); + + // Create name and add to msgbuf pool + hicn_name_copy(&tmp_name1, &name); + hicn_name_set_suffix(&tmp_name1, 1); + msgbuf_t *tmp_msgbuf1 = msgbuf_create(msgbuf_pool, CONN_ID_2, &tmp_name1); + + // Create (another) name and add to msgbuf pool + hicn_name_copy(&tmp_name2, &name); + hicn_name_set_suffix(&tmp_name2, 2); + msgbuf_t *tmp_msgbuf2 = msgbuf_create(msgbuf_pool, CONN_ID_2, &tmp_name2); + + // Add to packet cache (2 entries in the CS, 1 in the PIT) + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, MSGBUF_ID); + pkt_cache_add_to_pit(pkt_cache, tmp_msgbuf1, &tmp_name1); + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, tmp_msgbuf2, MSGBUF_ID_2); + + // Check stats (before clearing the packet cache) + ASSERT_EQ(pkt_cache_get_size(pkt_cache), 3u); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 2u); + ASSERT_EQ(cs->num_entries, 2); + ASSERT_EQ(cs->stats.lru.countAdds, 2u); + + // Clear packet cache (i.e. remove content packets from packet cache): + // PIT entry should still be there while CS entries are cleared + pkt_cache_cs_clear(pkt_cache); + cs = pkt_cache_get_cs(pkt_cache); + + // Check stats (after clearing the packet cache) + ASSERT_EQ(pkt_cache_get_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_pit_size(pkt_cache), 1u); + ASSERT_EQ(pkt_cache_get_cs_size(pkt_cache), 0u); + ASSERT_EQ(cs->num_entries, 0); + ASSERT_EQ(cs->stats.lru.countAdds, 0u); +} diff --git a/hicn-light/src/hicn/test/test-parser.cc b/hicn-light/src/hicn/test/test-parser.cc new file mode 100644 index 000000000..2e396b97d --- /dev/null +++ b/hicn-light/src/hicn/test/test-parser.cc @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +extern "C" { +#include <hicn/util/log.h> +#include <hicn/ctrl/parse.h> +} + +class ParserTest : public ::testing::Test { + protected: + ParserTest() { log_conf.log_level = LOG_INFO; } + virtual ~ParserTest() {} + + hc_command_t command_ = {}; +}; + +TEST_F(ParserTest, AddValidListener) { + std::string cmd = "add listener udp udp0 10.0.0.1 9695 eth0"; + + ASSERT_EQ(parse(cmd.c_str(), &command_), 0); + EXPECT_EQ(command_.object.listener.type, FACE_TYPE_UDP_LISTENER); + EXPECT_EQ(std::string(command_.object.listener.name), "udp0"); + EXPECT_EQ(command_.object.listener.family, AF_INET); + EXPECT_EQ(command_.object.listener.local_port, 9695); + EXPECT_EQ(std::string(command_.object.listener.interface_name), "eth0"); +} + +TEST_F(ParserTest, AddListenerSymbolicOverflow) { + std::string cmd = + "add listener udp super-long-symbolic-name 10.0.0.1 9696 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} + +TEST_F(ParserTest, AddListenerInvalidAddress) { + std::string cmd = "add listener udp udp0 10.0.0.0.1 9696 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} + +TEST_F(ParserTest, AddListenerInvalidAddressString) { + std::string cmd = "add listener udp udp0 invalid-addr 9696 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} + +TEST_F(ParserTest, AddListenerInvalidPortOutsideRange) { + std::string cmd = "add listener udp udp0 10.0.0.1 0 eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} + +TEST_F(ParserTest, AddListenerInvalidPortString) { + std::string cmd = "add listener udp udp0 10.0.0.1 invalid-port eth0"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} + +TEST_F(ParserTest, UnknownCommnad) { + std::string cmd = "add face"; + ASSERT_EQ(parse(cmd.c_str(), &command_), -1); +} diff --git a/hicn-light/src/hicn/test/test-probe_generator.cc b/hicn-light/src/hicn/test/test-probe_generator.cc new file mode 100644 index 000000000..4d6cfa8f7 --- /dev/null +++ b/hicn-light/src/hicn/test/test-probe_generator.cc @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <arpa/inet.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/strategies/probe_generator.h> +} + +class ProbeGeneratorTest : public ::testing::Test { + protected: + ProbeGeneratorTest() {} + + virtual ~ProbeGeneratorTest() {} +}; + +TEST_F(ProbeGeneratorTest, ProbeGeneratorRegisterProbe) { + probe_generator_t *pg = create_probe_generator(); + EXPECT_FALSE(pg == nullptr); + + register_probe(pg, 1); + register_probe(pg, 2); + register_probe(pg, 3); + register_probe(pg, 4); + + Ticks t = get_probe_send_time(pg, 1); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 2); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 3); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 4); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 5); + EXPECT_FALSE(t != 0); + + destroy_probe_generator(pg); +} + +TEST_F(ProbeGeneratorTest, ProbeGeneratorTime) { + probe_generator_t *pg = create_probe_generator(); + EXPECT_FALSE(pg == nullptr); + + Ticks t1 = register_probe(pg, 1); + Ticks t2 = get_probe_send_time(pg, 1); + + EXPECT_TRUE(t2 != 0); + EXPECT_TRUE(t1 == t2); + + destroy_probe_generator(pg); +} + +TEST_F(ProbeGeneratorTest, ProbeGeneratorDeleteProbe) { + probe_generator_t *pg = create_probe_generator(); + EXPECT_FALSE(pg == nullptr); + + register_probe(pg, 1); + register_probe(pg, 2); + register_probe(pg, 3); + register_probe(pg, 4); + + Ticks t = get_probe_send_time(pg, 1); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 2); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 3); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 4); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 5); + EXPECT_FALSE(t != 0); + + delete_probe(pg, 1); + delete_probe(pg, 3); + + t = get_probe_send_time(pg, 1); + EXPECT_FALSE(t != 0); + + t = get_probe_send_time(pg, 3); + EXPECT_FALSE(t != 0); + + destroy_probe_generator(pg); +} + +TEST_F(ProbeGeneratorTest, ProbeGeneratorDeleteAll) { + probe_generator_t *pg = create_probe_generator(); + EXPECT_FALSE(pg == nullptr); + + register_probe(pg, 1); + register_probe(pg, 2); + register_probe(pg, 3); + register_probe(pg, 4); + + Ticks t = get_probe_send_time(pg, 1); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 2); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 3); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 4); + EXPECT_TRUE(t != 0); + + t = get_probe_send_time(pg, 5); + EXPECT_FALSE(t != 0); + + delete_all_probes(pg); + + t = get_probe_send_time(pg, 1); + EXPECT_FALSE(t != 0); + + t = get_probe_send_time(pg, 2); + EXPECT_FALSE(t != 0); + + t = get_probe_send_time(pg, 3); + EXPECT_FALSE(t != 0); + + t = get_probe_send_time(pg, 4); + EXPECT_FALSE(t != 0); + + destroy_probe_generator(pg); +} diff --git a/hicn-light/src/hicn/test/test-strategy-best-path.cc b/hicn-light/src/hicn/test/test-strategy-best-path.cc new file mode 100644 index 000000000..f909c15f2 --- /dev/null +++ b/hicn-light/src/hicn/test/test-strategy-best-path.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/strategy.h> +#include <hicn/strategies/best_path.h> +} + +#define MAX_TESTS 10 + +#define NEXTHOP_ID1 NEXTHOP(28) +#define NEXTHOP_ID2 NEXTHOP(29) +#define UNKNOWN_ID1 NEXTHOP(0) +#define UNKNOWN_ID2 NEXTHOP(1) + +class StrategyBestpathTest : public ::testing::Test { + protected: + StrategyBestpathTest() { + /* Strategy and strategy entry */ + entry = { + .type = STRATEGY_TYPE_BESTPATH, + .options = + { + .bestpath = {}, + }, + .state = {.bestpath = {}}, + }; + + strategy_initialize(&entry, nullptr); + + // test init + EXPECT_EQ(entry.forwarder, nullptr); + EXPECT_EQ(entry.state.bestpath.best_nexthop, (unsigned)~0); + EXPECT_EQ(entry.state.bestpath.probing_state, PROBING_OFF); + + /* Available nexthops */ + available_nexthops_ = NEXTHOPS_EMPTY; + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)0); + + /* Message buffer */ + msgbuf_ = NULL; + ticks_ = ticks_now(); + } + + virtual ~StrategyBestpathTest() { strategy_finalize(&entry); } + + strategy_entry_t entry; + nexthops_t available_nexthops_; + msgbuf_t *msgbuf_; + Ticks ticks_; +}; + +TEST_F(StrategyBestpathTest, emptyNexthop) { + nexthops_t *nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)0); +} + +TEST_F(StrategyBestpathTest, faceExists) { + nexthops_t *nexthops; + + nexthops_add(&available_nexthops_, NEXTHOP_ID1); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + nexthops_add(&available_nexthops_, NEXTHOP_ID2); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)2); + + entry.state.bestpath.probing_state = PROBING_OFF; + entry.state.bestpath.best_nexthop = NEXTHOP_ID2; + + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID2)); + EXPECT_FALSE(nexthops_contains(nexthops, NEXTHOP_ID1)); + + EXPECT_TRUE(entry.state.bestpath.probing_state == PROBING_OFF); + EXPECT_EQ(entry.state.bestpath.best_nexthop, NEXTHOP_ID2); +} diff --git a/hicn-light/src/hicn/test/test-strategy-load-balancing.cc b/hicn-light/src/hicn/test/test-strategy-load-balancing.cc new file mode 100644 index 000000000..edac5669f --- /dev/null +++ b/hicn-light/src/hicn/test/test-strategy-load-balancing.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/strategy.h> +#include <hicn/strategies/random.h> +} + +#define MAX_TESTS 10 + +#define NEXTHOP_ID NEXTHOP(28) +#define NEXTHOP_ID2 NEXTHOP(29) +#define UNKNOWN_ID1 NEXTHOP(0) +#define UNKNOWN_ID2 NEXTHOP(1) + +class StrategyLoadBalancing : public ::testing::Test { + protected: + StrategyLoadBalancing() { + /* Strategy and strategy entry */ + entry = { + .type = STRATEGY_TYPE_LOADBALANCER, + .options = + { + .random = {}, + }, + .state = {.random = {}}, + }; + + strategy_initialize(&entry, nullptr); + + /* Available nexthops */ + available_nexthops_ = NEXTHOPS_EMPTY; + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)0); + + /* Message buffer */ + msgbuf_ = NULL; + ticks_ = ticks_now(); + } + virtual ~StrategyLoadBalancing() {} + + strategy_entry_t entry; + nexthops_t available_nexthops_; + msgbuf_t* msgbuf_; + Ticks ticks_; +}; + +TEST_F(StrategyLoadBalancing, SingleNexthop) { + /* Add a single nexthop */ + off_t id; + id = nexthops_add(&available_nexthops_, NEXTHOP_ID); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned nexthop; + for (unsigned i = 0; i < MAX_TESTS; i++) { + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, NEXTHOP_ID); + } + + /* Disable (move to nexthop unit tests) */ + nexthops_disable(nexthops, 0); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)0); + + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, INVALID_NEXTHOP); +} + +TEST_F(StrategyLoadBalancing, MultipleNexthops) { + off_t id, id2; + /* Add a single nexthop */ + id = nexthops_add(&available_nexthops_, NEXTHOP_ID); + id2 = nexthops_add(&available_nexthops_, NEXTHOP_ID2); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)2); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + strategy_add_nexthop(&entry, &available_nexthops_, id2); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)2); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID)); + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID2)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE((nexthops_contains(nexthops, NEXTHOP_ID)) || + (nexthops_contains(nexthops, NEXTHOP_ID2))); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned nexthop; + for (unsigned i = 0; i < MAX_TESTS; i++) { + nexthop = nexthops_get_one(nexthops); + EXPECT_TRUE((nexthop == NEXTHOP_ID) || (nexthop == NEXTHOP_ID2)); + } +} diff --git a/hicn-light/src/hicn/test/test-strategy-local-remote.cc b/hicn-light/src/hicn/test/test-strategy-local-remote.cc new file mode 100644 index 000000000..6693e29c8 --- /dev/null +++ b/hicn-light/src/hicn/test/test-strategy-local-remote.cc @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/base/loop.h> +#include <hicn/config/configuration.h> +#include <hicn/core/forwarder.h> +#include <hicn/core/listener.h> +#include <hicn/core/address_pair.h> +#include <hicn/core/address.h> +#include <hicn/core/strategy.h> +#include <hicn/strategies/local_remote.h> +} + +class StrategyLocalRemoteTest : public ::testing::Test { + protected: + StrategyLocalRemoteTest() { + conf_ = configuration_create(); + MAIN_LOOP = loop_create(); + fwd_ = forwarder_create(conf_); + + /* Strategy and strategy entry */ + entry_ = { + .type = STRATEGY_TYPE_LOCAL_REMOTE, + .options = + { + .random = {}, + }, + .state = {.random = {}}, + }; + + strategy_initialize(&entry_, fwd_); + } + + virtual ~StrategyLocalRemoteTest() { + INFO("loop stopped"); + forwarder_free(fwd_); + loop_free(MAIN_LOOP); + MAIN_LOOP = NULL; + strategy_finalize(&entry_); + } + + strategy_entry_t entry_; + nexthops_t available_nexthops_ = NEXTHOPS_EMPTY; + configuration_t* conf_; + forwarder_t* fwd_; + msgbuf_t msgbuf_; +}; + +TEST_F(StrategyLocalRemoteTest, InputLocalOutputLocal) { + address_t prod_addr = ADDRESS4_LOCALHOST(12345); + address_t cons_addr = ADDRESS4_LOCALHOST(12346); + address_t listener_addr = ADDRESS4_LOCALHOST(9596); + + listener_t* listener = listener_create(FACE_TYPE_UDP_LISTENER, &listener_addr, + "lo", "lo_udp4", fwd_); + + address_pair_t pair_conn_prod = { + .local = listener_addr, + .remote = prod_addr, + }; + + address_pair_t pair_conn_cons = { + .local = listener_addr, + .remote = cons_addr, + }; + + unsigned prod_conn_id = + listener_create_connection(listener, "conp", &pair_conn_prod); + unsigned cons_conn_id = + listener_create_connection(listener, "conc", &pair_conn_cons); + + msgbuf_.connection_id = cons_conn_id; + + nexthops_add(&available_nexthops_, prod_conn_id); + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry_, &available_nexthops_, &msgbuf_); + + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)0); +} + +TEST_F(StrategyLocalRemoteTest, InputRemoteOutputRemote) { + address_t prod_addr = ADDRESS4_LOCALHOST(12345); + address_t cons_addr = ADDRESS4_LOCALHOST(12346); + address_t listener_addr = ADDRESS4_LOCALHOST(9596); + + listener_t* listener = listener_create(FACE_TYPE_UDP_LISTENER, &listener_addr, + "lo", "lo_udp4", fwd_); + + address_pair_t pair_conn_prod = { + .local = listener_addr, + .remote = prod_addr, + }; + + address_pair_t pair_conn_cons = { + .local = listener_addr, + .remote = cons_addr, + }; + + connection_t* conn; + unsigned prod_conn_id = + listener_create_connection(listener, "conp", &pair_conn_prod); + unsigned cons_conn_id = + listener_create_connection(listener, "conc", &pair_conn_cons); + + // fake two remote connections + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + prod_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = false; + + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + cons_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = false; + + msgbuf_.connection_id = cons_conn_id; + + nexthops_add(&available_nexthops_, prod_conn_id); + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry_, &available_nexthops_, &msgbuf_); + + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)0); +} + +TEST_F(StrategyLocalRemoteTest, InputLocalOutputRemote) { + address_t prod_addr = ADDRESS4_LOCALHOST(12345); + address_t cons_addr = ADDRESS4_LOCALHOST(12346); + address_t listener_addr = ADDRESS4_LOCALHOST(9596); + + listener_t* listener = listener_create(FACE_TYPE_UDP_LISTENER, &listener_addr, + "lo", "lo_udp4", fwd_); + + address_pair_t pair_conn_prod = { + .local = listener_addr, + .remote = prod_addr, + }; + + address_pair_t pair_conn_cons = { + .local = listener_addr, + .remote = cons_addr, + }; + + connection_t* conn; + unsigned prod_conn_id = + listener_create_connection(listener, "conp", &pair_conn_prod); + unsigned cons_conn_id = + listener_create_connection(listener, "conc", &pair_conn_cons); + + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + prod_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = false; + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + cons_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = true; + + msgbuf_.connection_id = cons_conn_id; + + nexthops_add(&available_nexthops_, prod_conn_id); + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry_, &available_nexthops_, &msgbuf_); + + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); +} + +TEST_F(StrategyLocalRemoteTest, InputRemoteOutputLocal) { + address_t cons_addr = ADDRESS4_LOCALHOST(12345); + address_t prod_addr = ADDRESS4_LOCALHOST(12346); + address_t listener_addr = ADDRESS4_LOCALHOST(9695); + + listener_t* listener = listener_create(FACE_TYPE_UDP_LISTENER, &listener_addr, + "lo", "lo_udp4", fwd_); + + address_pair_t pair_conn_prod = { + .local = listener_addr, + .remote = prod_addr, + }; + + address_pair_t pair_conn_cons = { + .local = listener_addr, + .remote = cons_addr, + }; + + connection_t* conn; + unsigned prod_conn_id = + listener_create_connection(listener, "conp", &pair_conn_prod); + unsigned cons_conn_id = + listener_create_connection(listener, "conc", &pair_conn_cons); + + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + prod_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = true; + conn = connection_table_get_by_id(forwarder_get_connection_table(fwd_), + cons_conn_id); + ASSERT_TRUE(conn != NULL); + conn->local = false; + + msgbuf_.connection_id = cons_conn_id; + + nexthops_add(&available_nexthops_, prod_conn_id); + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry_, &available_nexthops_, &msgbuf_); + + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); +} diff --git a/hicn-light/src/hicn/test/test-strategy-random.cc b/hicn-light/src/hicn/test/test-strategy-random.cc new file mode 100644 index 000000000..bd9b70120 --- /dev/null +++ b/hicn-light/src/hicn/test/test-strategy-random.cc @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/strategy.h> +#include <hicn/core/strategy_vft.h> +#include <hicn/strategies/random.h> +} + +#define MAX_TESTS 10 + +#define NEXTHOP_ID NEXTHOP(28) +#define UNKNOWN_ID1 NEXTHOP(0) +#define UNKNOWN_ID2 NEXTHOP(1) + +class StrategyRandomTest : public ::testing::Test { + protected: + StrategyRandomTest() { + /* Strategy and strategy entry */ + entry = { + .type = STRATEGY_TYPE_RANDOM, + .options = + { + .random = {}, + }, + .state = {.random = {}}, + }; + + strategy_initialize(&entry, nullptr); + + /* Available nexthops */ + available_nexthops_ = NEXTHOPS_EMPTY; + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)0); + + /* Message buffer */ + msgbuf_ = NULL; + ticks_ = ticks_now(); + } + virtual ~StrategyRandomTest() {} + + strategy_entry_t entry; + nexthops_t available_nexthops_; + msgbuf_t* msgbuf_; + Ticks ticks_; +}; + +TEST_F(StrategyRandomTest, SingleNexthop) { + off_t id; + + /* Add a single nexthop */ + id = nexthops_add(&available_nexthops_, NEXTHOP_ID); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned nexthop; + for (unsigned i = 0; i < MAX_TESTS; i++) { + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, NEXTHOP_ID); + } + + /* Disable (move to nexthop unit tests) */ + nexthops_disable(nexthops, 0); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)0); + + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, INVALID_NEXTHOP); +} + +TEST_F(StrategyRandomTest, MultipleNexthops) { + off_t id; + + /* Add a single nexthop */ + id = nexthops_add(&available_nexthops_, NEXTHOP_ID); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned nexthop; + for (unsigned i = 0; i < MAX_TESTS; i++) { + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, NEXTHOP_ID); + } +} diff --git a/hicn-light/src/hicn/test/test-strategy-replication.cc b/hicn-light/src/hicn/test/test-strategy-replication.cc new file mode 100644 index 000000000..1c4d824b8 --- /dev/null +++ b/hicn-light/src/hicn/test/test-strategy-replication.cc @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <netinet/in.h> + +extern "C" { +#define WITH_TESTS +#include <hicn/core/strategy.h> +#include <hicn/strategies/random.h> +} + +#define MAX_TESTS 10 + +#define NEXTHOP_ID1 NEXTHOP(28) +#define NEXTHOP_ID2 NEXTHOP(29) +#define UNKNOWN_ID1 NEXTHOP(0) +#define UNKNOWN_ID2 NEXTHOP(1) + +class StrategyReplicationTest : public ::testing::Test { + protected: + StrategyReplicationTest() { + /* Strategy and strategy entry */ + entry = { + .type = STRATEGY_TYPE_REPLICATION, + .options = + { + .replication = {}, + }, + .state = {.replication = {}}, + }; + + strategy_initialize(&entry, nullptr); + + /* Available nexthops */ + available_nexthops_ = NEXTHOPS_EMPTY; + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)0); + + /* Message buffer */ + msgbuf_ = NULL; + ticks_ = ticks_now(); + } + + virtual ~StrategyReplicationTest() { strategy_finalize(&entry); } + + strategy_entry_t entry; + nexthops_t available_nexthops_; + msgbuf_t* msgbuf_; + Ticks ticks_; +}; + +TEST_F(StrategyReplicationTest, SingleNexthop) { + off_t id; + + /* Add a single nexthop */ + id = nexthops_add(&available_nexthops_, NEXTHOP_ID1); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)1); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned nexthop; + for (unsigned i = 0; i < MAX_TESTS; i++) { + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, NEXTHOP_ID1); + } + + /* Disable (move to nexthop unit tests) */ + nexthops_disable(nexthops, 0); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)0); + + nexthop = nexthops_get_one(nexthops); + EXPECT_EQ(nexthop, INVALID_NEXTHOP); +} + +TEST_F(StrategyReplicationTest, MultipleNexthops) { + off_t id; + + id = nexthops_add(&available_nexthops_, NEXTHOP_ID1); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)1); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)1); + + id = nexthops_add(&available_nexthops_, NEXTHOP_ID2); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)2); + + strategy_add_nexthop(&entry, &available_nexthops_, id); + EXPECT_EQ(nexthops_get_len(&available_nexthops_), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(&available_nexthops_), (size_t)2); + + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID1)); + EXPECT_TRUE(nexthops_contains(&available_nexthops_, NEXTHOP_ID2)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(&available_nexthops_, UNKNOWN_ID2)); + + /* Lookup */ + nexthops_t* nexthops; + nexthops = strategy_lookup_nexthops(&entry, &available_nexthops_, msgbuf_); + + EXPECT_EQ(nexthops_get_len(nexthops), (size_t)2); + EXPECT_EQ(nexthops_get_curlen(nexthops), (size_t)2); + + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID1)); + EXPECT_TRUE(nexthops_contains(nexthops, NEXTHOP_ID2)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID1)); + EXPECT_FALSE(nexthops_contains(nexthops, UNKNOWN_ID2)); + + /* Retrieve candidate */ + + unsigned tests = 0; + nexthops_foreach(nexthops, nexthop, { + EXPECT_TRUE(nexthop == NEXTHOP_ID1 || nexthop == NEXTHOP_ID2); + tests++; + }); + + EXPECT_EQ(tests, (unsigned)2); +} diff --git a/hicn-light/src/hicn/test/test-subscription.cc b/hicn-light/src/hicn/test/test-subscription.cc new file mode 100644 index 000000000..5fd3ab57d --- /dev/null +++ b/hicn-light/src/hicn/test/test-subscription.cc @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include <gtest/gtest.h> + +extern "C" { +#include <hicn/core/subscription.h> +#include <hicn/util/vector.h> +} + +static inline unsigned CONN_ID = 1; +static inline unsigned CONN_ID_2 = 2; + +class SubscriptionTest : public ::testing::Test { + protected: + SubscriptionTest() { subscriptions = subscription_table_create(); } + virtual ~SubscriptionTest() { subscription_table_free(subscriptions); } + + subscription_table_t *subscriptions; +}; + +TEST_F(SubscriptionTest, CreateSubscriptionTable) { + // Check subscription table allocation + ASSERT_NE(subscriptions, nullptr); +} + +TEST_F(SubscriptionTest, SetTopic) { + hc_topics_t topics = TOPIC_STRATEGY; + + // Check that only the topic desired has been subscribed to + for (int topic = TOPIC_UNDEFINED; topic < TOPIC_N; topic <<= 1) { + if (topic == TOPIC_STRATEGY) { + EXPECT_TRUE(topics_contains(topics, (hc_topic_t)topic)); + continue; + } + EXPECT_FALSE(topics_contains(topics, (hc_topic_t)topic)); + } +} + +TEST_F(SubscriptionTest, GetObjectFromTopic) { + hc_object_type_t object_type = object_from_topic(TOPIC_STRATEGY); + EXPECT_EQ(object_type, OBJECT_TYPE_STRATEGY); + + object_type = object_from_topic(TOPIC_FACE); + EXPECT_EQ(object_type, OBJECT_TYPE_FACE); +} + +TEST_F(SubscriptionTest, AddSubscription) { + hc_topics_t topics = TOPIC_STRATEGY; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, topics); +} + +TEST_F(SubscriptionTest, AddAndRemoveSubscriptionForAllTopics) { + hc_topics_t topics = ALL_TOPICS; + int ret = subscription_table_add_topics_for_connection(subscriptions, + ALL_TOPICS, CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + int num_subscriptions_removed = + subscription_table_remove_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(num_subscriptions_removed, NUM_TOPICS); + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, 0u); +} + +// Failure while adding subscription cannot be tested since it depends on vector +// reallocation + +TEST_F(SubscriptionTest, AddSubscriptionAlreadyAdded) { + hc_topics_t topics = TOPIC_STRATEGY; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + // Subscribe again to same topic + ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, -2); // -2 = already-added subscription + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, topics); +} + +TEST_F(SubscriptionTest, GetSubscriptionsForConnectionWithoutSubscriptions) { + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, (hc_topics_t)0); +} + +TEST_F(SubscriptionTest, GetSubscriptionsForConnectionWithMultipleSubs) { + hc_topics_t topics = TOPIC_STRATEGY | TOPIC_FACE; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, topics); + + // Add another subscription + ret = subscription_table_add_topics_for_connection(subscriptions, TOPIC_PROBE, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, topics |= TOPIC_PROBE); +} + +TEST_F(SubscriptionTest, RemoveSubscription) { + // Add subscriptions + hc_topics_t topics = TOPIC_STRATEGY | TOPIC_FACE; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + // Remove one of the previously added subscriptions + int num_subscriptions_removed = + subscription_table_remove_topics_for_connection(subscriptions, + TOPIC_STRATEGY, CONN_ID); + EXPECT_EQ(num_subscriptions_removed, 1); + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, TOPIC_FACE); +} + +TEST_F(SubscriptionTest, RemoveMultipleSubscriptions) { + // Add subscriptions + hc_topics_t topics = TOPIC_STRATEGY | TOPIC_FACE | TOPIC_PROBE; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + // Remove two of the previously added subscriptions + int num_subscriptions_removed = + subscription_table_remove_topics_for_connection( + subscriptions, TOPIC_STRATEGY | TOPIC_FACE, CONN_ID); + EXPECT_EQ(num_subscriptions_removed, 2); + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, TOPIC_PROBE); +} + +TEST_F(SubscriptionTest, RemoveNonRegistredSubscription) { + // Remove a subscription that is not present + int num_subscriptions_removed = + subscription_table_remove_topics_for_connection(subscriptions, + TOPIC_PROBE, CONN_ID); + EXPECT_EQ(num_subscriptions_removed, 0); + + // Add two new subscriptions + hc_topics_t topics = TOPIC_STRATEGY | TOPIC_FACE; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + // Remove subscription that was not registred previously + num_subscriptions_removed = subscription_table_remove_topics_for_connection( + subscriptions, TOPIC_PROBE, CONN_ID); + EXPECT_EQ(num_subscriptions_removed, 0); + + hc_topics_t topics_ret = + subscription_table_get_topics_for_connection(subscriptions, CONN_ID); + EXPECT_EQ(topics_ret, topics); +} + +TEST_F(SubscriptionTest, GetConnectionsForSubscription) { + // Add subscriptions for two connections + hc_topics_t topics = TOPIC_STRATEGY | TOPIC_FACE; + int ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID); + EXPECT_EQ(ret, 0); // 0 = success + + topics = TOPIC_STRATEGY; + ret = subscription_table_add_topics_for_connection(subscriptions, topics, + CONN_ID_2); + EXPECT_EQ(ret, 0); // 0 = success + + // Check the connections associated with the strategy topic + unsigned *conn_ids = subscription_table_get_connections_for_topic( + subscriptions, TOPIC_STRATEGY); + EXPECT_EQ(vector_len(conn_ids), 2u); + EXPECT_TRUE(conn_ids[0] == CONN_ID || conn_ids[0] == CONN_ID_2); + EXPECT_TRUE(conn_ids[1] == CONN_ID || conn_ids[1] == CONN_ID_2); + + // Check the connections associated with the face topic + conn_ids = + subscription_table_get_connections_for_topic(subscriptions, TOPIC_FACE); + EXPECT_EQ(vector_len(conn_ids), 1u); + EXPECT_EQ(conn_ids[0], (unsigned)CONN_ID); +} diff --git a/hicn-light/src/hicn/test/test-utils.h b/hicn-light/src/hicn/test/test-utils.h new file mode 100644 index 000000000..577629584 --- /dev/null +++ b/hicn-light/src/hicn/test/test-utils.h @@ -0,0 +1,26 @@ +#pragma once + +#include <vector> +#include <thread> +#include <numeric> + +static constexpr int N_RUNS = 100; + +// Utility function for time execution calculation +template <typename F, typename... Args> +double get_execution_time(F func, Args &&...args) { + std::vector<float> execution_times; + + for (int i = 0; i < N_RUNS; i++) { + auto start = std::chrono::high_resolution_clock::now(); + func(std::forward<Args>(args)...); + auto end = std::chrono::high_resolution_clock::now(); + + std::chrono::duration<double, std::milli> ms = end - start; + execution_times.emplace_back(ms.count()); + } + + // Calculate average + return std::reduce(execution_times.begin(), execution_times.end()) / + execution_times.size(); +}
\ No newline at end of file diff --git a/hicn-light/src/hicn/test/test_hash.cc b/hicn-light/src/hicn/test/test_hash.cc new file mode 100644 index 000000000..c742aa248 --- /dev/null +++ b/hicn-light/src/hicn/test/test_hash.cc @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <unordered_set> +#include <hicn/test/test-utils.h> + +extern "C" { +#include <hicn/util/hash.h> +#include <hicn/core/address_pair.h> +#include <hicn/core/listener.h> +} + +static constexpr uint32_t init_val = 2166136261UL; +static constexpr int N_HASHES = 50000; + +TEST(HashTest, MultipleHashesForSameAddrPair) { + address_pair_t pair = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + + unsigned h1 = hash_struct(&pair); + unsigned h2 = hash_struct(&pair); + EXPECT_EQ(h1, h2); +} + +TEST(HashTest, SameAddrPairs) { + address_pair_t pair1 = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + address_pair_t pair2 = pair1; + + unsigned h1 = hash_struct(&pair1); + unsigned h2 = hash_struct(&pair2); + EXPECT_EQ(h1, h2); +} + +TEST(HashTest, DifferentAddrPairs) { + address_pair_t pair1 = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + + address_pair_t pair2 = + address_pair_factory(_ADDRESS4_LOCALHOST(3), _ADDRESS4_LOCALHOST(4)); + + unsigned h1 = hash_struct(&pair1); + unsigned h2 = hash_struct(&pair2); + EXPECT_NE(h1, h2); +} + +TEST(HashTest, SameLocalDifferentRemote) { + address_pair_t pair1 = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + + address_pair_t pair2 = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(4)); + + unsigned h1 = hash_struct(&pair1); + unsigned h2 = hash_struct(&pair2); + EXPECT_NE(h1, h2); +} + +TEST(HashTest, SameRemoteDifferentLocal) { + address_pair_t pair1 = + address_pair_factory(_ADDRESS4_LOCALHOST(1), _ADDRESS4_LOCALHOST(2)); + + address_pair_t pair2 = + address_pair_factory(_ADDRESS4_LOCALHOST(3), _ADDRESS4_LOCALHOST(2)); + + unsigned h1 = hash_struct(&pair1); + unsigned h2 = hash_struct(&pair2); + EXPECT_NE(h1, h2); +} + +TEST(HashTest, SameAddresses) { + address_t addr1 = _ADDRESS4_LOCALHOST(1); + address_t addr2 = _ADDRESS4_LOCALHOST(1); + + unsigned h1 = hash_struct(&addr1); + unsigned h2 = hash_struct(&addr2); + + EXPECT_EQ(h1, h2); +} + +TEST(HashTest, SameListenerKeys) { + listener_key_t key1 = + listener_key_factory(_ADDRESS4_LOCALHOST(1), FACE_TYPE_UDP_LISTENER); + listener_key_t key2 = + listener_key_factory(_ADDRESS4_LOCALHOST(1), FACE_TYPE_UDP_LISTENER); + + unsigned h1 = hash_struct(&key1); + unsigned h2 = hash_struct(&key2); + + EXPECT_EQ(h1, h2); +} + +TEST(HashTest, Collisions) { + std::unordered_set<uint32_t> hashes; + int n_collisions = 0; + for (int i = 0; i < 50000; i++) { + uint32_t seg = i; + // u32 h = utils::hash::fnv32_buf(&seg, sizeof(seg)); + // u32 h = cumulative_hash32(&seg, sizeof(uint32_t), init_val); + u32 h = hash(&seg, sizeof(seg)); + + if (hashes.find(h) != hashes.end()) n_collisions++; + hashes.insert(h); + } + EXPECT_EQ(n_collisions, 0); +} + +/*** Compare FNV with Jenkins ***/ + +typedef struct { + uint32_t data[6]; +} small_struct_t; // Same size as 'NameBitvector' + +typedef struct { + uint64_t data[32]; +} big_struct_t; // Same size as 'address_pair_t' + +TEST(HashTest, PerformanceComparisonSmallStruct) { + small_struct_t small_struct; + + // FNV + auto time_fnv = get_execution_time([&]() { + for (int i = 0; i < N_HASHES; i++) { + small_struct.data[0] = i; + cumulative_hash32(&small_struct, sizeof(small_struct_t), init_val); + } + }); + + // Jenkins + auto time_jenkins = get_execution_time([&]() { + for (int i = 0; i < N_HASHES; i++) { + small_struct.data[0] = i; + hash(&small_struct, sizeof(small_struct_t)); + } + }); + + std::cout << "Small struct (size = " << sizeof(small_struct_t) << " bytes)\n"; + std::cout << "FNV: " << time_fnv << "ms\n"; + std::cout << "Jenkins: " << time_jenkins << "ms\n"; +} + +TEST(HashTest, PerformanceComparisonBigStruct) { + big_struct_t big_struct; + + // FNV + auto time_fnv = get_execution_time([&]() { + for (int i = 0; i < N_HASHES; i++) { + big_struct.data[0] = i; + cumulative_hash32(&big_struct, sizeof(big_struct_t), init_val); + } + }); + + // Jenkins + auto time_jenkins = get_execution_time([&]() { + for (int i = 0; i < N_HASHES; i++) { + big_struct.data[0] = i; + hash(&big_struct, sizeof(big_struct_t)); + } + }); + + std::cout << "Big struct (size = " << sizeof(big_struct_t) << " bytes)\n"; + std::cout << "FNV: " << time_fnv << "ms\n"; + std::cout << "Jenkins: " << time_jenkins << "ms\n"; +} + +TEST(HashTest, CollisionsComparison) { + small_struct_t small_struct = {0}; + std::unordered_set<uint32_t> hashes; + int n_collisions_fnv = 0, n_collisions_jenkins = 0; + + // FNV + for (int i = 0; i < 10 * N_HASHES; i++) { + small_struct.data[0] = i; + uint32_t h = + cumulative_hash32(&small_struct, sizeof(small_struct_t), init_val); + + if (hashes.find(h) != hashes.end()) n_collisions_fnv++; + hashes.insert(h); + } + + hashes.clear(); + + // Jenkins + for (int i = 0; i < 10 * N_HASHES; i++) { + small_struct.data[0] = i; + uint32_t h = hash(&small_struct, sizeof(small_struct_t)); + + if (hashes.find(h) != hashes.end()) n_collisions_jenkins++; + hashes.insert(h); + } + + std::cout << "Small struct (size = " << sizeof(small_struct_t) << " bytes)\n"; + std::cout << "FNV: " << n_collisions_fnv << " collision/s\n"; + std::cout << "Jenkins: " << n_collisions_jenkins << " collision/s\n"; +}
\ No newline at end of file |