aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/hicn/test
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/hicn/test')
-rw-r--r--hicn-light/src/hicn/test/CMakeLists.txt40
-rw-r--r--hicn-light/src/hicn/test/main.cc21
-rw-r--r--hicn-light/src/hicn/test/test-configuration.cc83
-rw-r--r--hicn-light/src/hicn/test/test-connection_table.cc301
-rw-r--r--hicn-light/src/hicn/test/test-ctrl.cc197
-rw-r--r--hicn-light/src/hicn/test/test-fib.cc338
-rw-r--r--hicn-light/src/hicn/test/test-listener_table.cc244
-rw-r--r--hicn-light/src/hicn/test/test-local_prefixes.cc252
-rw-r--r--hicn-light/src/hicn/test/test-loop.cc294
-rw-r--r--hicn-light/src/hicn/test/test-msgbuf_pool.cc191
-rw-r--r--hicn-light/src/hicn/test/test-nexthops.cc274
-rw-r--r--hicn-light/src/hicn/test/test-packet_cache.cc703
-rw-r--r--hicn-light/src/hicn/test/test-parser.cc71
-rw-r--r--hicn-light/src/hicn/test/test-probe_generator.cc154
-rw-r--r--hicn-light/src/hicn/test/test-strategy-best-path.cc107
-rw-r--r--hicn-light/src/hicn/test/test-strategy-load-balancing.cc152
-rw-r--r--hicn-light/src/hicn/test/test-strategy-local-remote.cc233
-rw-r--r--hicn-light/src/hicn/test/test-strategy-random.cc150
-rw-r--r--hicn-light/src/hicn/test/test-strategy-replication.cc162
-rw-r--r--hicn-light/src/hicn/test/test-subscription.cc204
-rw-r--r--hicn-light/src/hicn/test/test-utils.h26
-rw-r--r--hicn-light/src/hicn/test/test_hash.cc216
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