aboutsummaryrefslogtreecommitdiffstats
path: root/libtransport/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'libtransport/src/test')
-rw-r--r--libtransport/src/test/CMakeLists.txt81
-rw-r--r--libtransport/src/test/main.cc21
-rw-r--r--libtransport/src/test/packet_samples.h71
-rw-r--r--libtransport/src/test/test_aggregated_header.cc624
-rw-r--r--libtransport/src/test/test_auth.cc325
-rw-r--r--libtransport/src/test/test_consumer_producer_rtc.cc172
-rw-r--r--libtransport/src/test/test_core_manifest.cc312
-rw-r--r--libtransport/src/test/test_event_thread.cc95
-rw-r--r--libtransport/src/test/test_fec_base_rely.cc454
-rw-r--r--libtransport/src/test/test_fec_base_rs.cc413
-rw-r--r--libtransport/src/test/test_fec_reedsolomon.cc269
-rw-r--r--libtransport/src/test/test_fec_rely_wrapper.cc247
-rw-r--r--libtransport/src/test/test_fixed_block_allocator.cc211
-rw-r--r--libtransport/src/test/test_indexer.cc322
-rw-r--r--libtransport/src/test/test_interest.cc308
-rw-r--r--libtransport/src/test/test_memif_connector.cc152
-rw-r--r--libtransport/src/test/test_packet.cc932
-rw-r--r--libtransport/src/test/test_packet_allocator.cc132
-rw-r--r--libtransport/src/test/test_prefix.cc334
-rw-r--r--libtransport/src/test/test_quadloop.cc176
-rw-r--r--libtransport/src/test/test_quality_score.cc135
-rw-r--r--libtransport/src/test/test_sessions.cc77
-rw-r--r--libtransport/src/test/test_thread_pool.cc70
-rw-r--r--libtransport/src/test/test_traffic_generator.cc119
24 files changed, 6052 insertions, 0 deletions
diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt
new file mode 100644
index 000000000..56edcd102
--- /dev/null
+++ b/libtransport/src/test/CMakeLists.txt
@@ -0,0 +1,81 @@
+# 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.
+
+##############################################################
+# Test sources
+##############################################################
+list(APPEND TESTS_SRC
+ main.cc
+ test_aggregated_header.cc
+ test_auth.cc
+ test_consumer_producer_rtc.cc
+ test_core_manifest.cc
+ # test_event_thread.cc
+ test_fec_base_rs.cc
+ test_fec_reedsolomon.cc
+ test_fixed_block_allocator.cc
+ test_indexer.cc
+ test_interest.cc
+ test_packet.cc
+ test_packet_allocator.cc
+ test_quality_score.cc
+ test_sessions.cc
+ test_thread_pool.cc
+ test_quadloop.cc
+ test_prefix.cc
+ test_traffic_generator.cc
+)
+
+if (ENABLE_RELY)
+ list(APPEND TESTS_SRC
+ test_fec_rely_wrapper.cc
+ test_fec_base_rely.cc
+ )
+endif()
+
+#if (UNIX AND NOT APPLE)
+# list(APPEND TESTS_SRC
+# test_memif_connector.cc
+# )
+#endif()
+
+
+##############################################################
+# Link libraries
+##############################################################
+set(MEMIF_MODULE_LIBRARIES
+ ${LIBRARIES}
+ ${LIBTRANSPORT_SHARED}
+ ${GTEST_LIBRARIES}
+)
+
+
+##############################################################
+# Build single unit test executable and add it to test list
+##############################################################
+build_executable(libtransport_tests
+ NO_INSTALL
+ SOURCES ${TESTS_SRC}
+ LINK_LIBRARIES
+ ${MEMIF_MODULE_LIBRARIES}
+ INCLUDE_DIRS
+ $<TARGET_PROPERTY:${LIBTRANSPORT_SHARED},INCLUDE_DIRECTORIES>
+ ${GTEST_INCLUDE_DIRS}
+ DEPENDS gtest ${LIBTRANSPORT_SHARED}
+ COMPONENT ${LIBTRANSPORT_COMPONENT}
+ DEFINITIONS ${COMPILER_DEFINITIONS}
+ COMPILE_OPTIONS ${COMPILER_OPTIONS}
+ LINK_FLAGS ${LINK_FLAGS}
+)
+
+add_test_internal(libtransport_tests)
diff --git a/libtransport/src/test/main.cc b/libtransport/src/test/main.cc
new file mode 100644
index 000000000..591ed0d5b
--- /dev/null
+++ b/libtransport/src/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();
+} \ No newline at end of file
diff --git a/libtransport/src/test/packet_samples.h b/libtransport/src/test/packet_samples.h
new file mode 100644
index 000000000..216ac7f9f
--- /dev/null
+++ b/libtransport/src/test/packet_samples.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#define TCP_PROTO 0x06
+#define ICMP_PROTO 0x01
+#define ICMP6_PROTO 0x3a
+
+#define IPV6_HEADER(next_header, payload_length) \
+ 0x60, 0x00, 0x00, 0x00, 0x00, payload_length, next_header, 0x40, 0xb0, 0x06, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd, 0xab, \
+ 0xcd, 0xef, 0xb0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xca
+
+#define IPV4_HEADER(next_header, payload_length) \
+ 0x45, 0x02, 0x00, payload_length + 20, 0x47, 0xc4, 0x40, 0x00, 0x25, \
+ next_header, 0x6e, 0x76, 0x03, 0x7b, 0xd9, 0xd0, 0xc0, 0xa8, 0x01, 0x5c
+
+#define TCP_HEADER(flags) \
+ 0x12, 0x34, 0x43, 0x21, 0x00, 0x00, 0x00, 0x01, 0xb2, 0x8c, 0x03, 0x1f, \
+ 0x80, flags, 0x00, 0x0a, 0xb9, 0xbb, 0x00, 0x00
+
+#define PAYLOAD \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x20, 0x00, 0x00
+
+#define PAYLOAD_SIZE 12
+
+#define ICMP_ECHO_REQUEST \
+ 0x08, 0x00, 0x87, 0xdb, 0x38, 0xa7, 0x00, 0x05, 0x60, 0x2b, 0xc2, 0xcb, \
+ 0x00, 0x02, 0x29, 0x7c, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, \
+ 0x34, 0x35, 0x36, 0x37
+
+#define ICMP6_ECHO_REQUEST \
+ 0x80, 0x00, 0x86, 0x3c, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, \
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
+
+#define AH_HEADER \
+ 0x00, (128 >> 2), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define SIGNATURE \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
diff --git a/libtransport/src/test/test_aggregated_header.cc b/libtransport/src/test/test_aggregated_header.cc
new file mode 100644
index 000000000..4dd71f60d
--- /dev/null
+++ b/libtransport/src/test/test_aggregated_header.cc
@@ -0,0 +1,624 @@
+/*
+ * 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 <hicn/transport/errors/not_implemented_exception.h>
+#include <protocols/rtc/rtc_packet.h>
+#include <test/packet_samples.h>
+
+#include <climits>
+#include <random>
+#include <vector>
+
+namespace transport {
+
+namespace core {
+
+namespace {
+// The fixture for testing class Foo.
+class AggregatedPktHeaderTest : public ::testing::Test {
+ protected:
+ AggregatedPktHeaderTest() {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~AggregatedPktHeaderTest() {
+ // 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).
+ }
+};
+
+} // namespace
+
+#if 0
+TEST_F(AggregatedPktHeaderTest, Add2Packets8bit) {
+ uint8_t buf[1500];
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17};
+ uint16_t pkt2_len = 7;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt1_len, 2);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ uint8_t* ptr = hdr.getPayloadAppendPtr();
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ *(ptr + i) = pkt1[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ *(ptr + i + pkt1_len) = pkt2[i];
+ }
+
+ // print
+ // for (uint16_t i = 0; i < 40; i++){
+ // std::cout << (int) i << " " << (int) buf[i] << std::endl;
+ //}
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+}
+#endif
+
+TEST_F(AggregatedPktHeaderTest, Add2Packets8bit255) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14}; // 255
+ uint16_t pkt2_len = 255;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt2_len, 2);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ uint8_t* ptr = hdr.getPayloadAppendPtr();
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ *(ptr + i) = pkt1[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ *(ptr + i + pkt1_len) = pkt2[i];
+ }
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+}
+
+TEST_F(AggregatedPktHeaderTest, Add2Packets8bit256) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15}; // 256
+ uint16_t pkt2_len = 256;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt2_len, 2);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ uint8_t* ptr = hdr.getPayloadAppendPtr();
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ *(ptr + i) = pkt1[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ *(ptr + i + pkt1_len) = pkt2[i];
+ }
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+}
+
+TEST_F(AggregatedPktHeaderTest, Add4Packets8bit) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17};
+ uint16_t pkt2_len = 7;
+
+ std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
+ uint16_t pkt3_len = 10;
+
+ std::vector<uint8_t> pkt4 = {100, 110};
+ uint16_t pkt4_len = 2;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt1_len, 4);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ hdr.addPacketToHeader(2, pkt3_len);
+ hdr.addPacketToHeader(3, pkt4_len);
+ uint8_t* ptr = hdr.getPayloadAppendPtr();
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ *(ptr + i) = pkt1[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ *(ptr + i + pkt1_len) = pkt2[i];
+ }
+
+ // copy packet 3
+ for (uint16_t i = 0; i < pkt3_len; i++) {
+ *(ptr + i + pkt1_len + pkt2_len) = pkt3[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt4_len; i++) {
+ *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i];
+ }
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+
+ hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt3_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt3[i]);
+ }
+
+ hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt4_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt4[i]);
+ }
+}
+
+TEST_F(AggregatedPktHeaderTest, Add4Packets16bit) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17};
+ uint16_t pkt2_len = 7;
+
+ std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260
+ uint16_t pkt3_len = 260;
+
+ std::vector<uint8_t> pkt4 = {100, 110};
+ uint16_t pkt4_len = 2;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt3_len, 4);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ hdr.addPacketToHeader(2, pkt3_len);
+ hdr.addPacketToHeader(3, pkt4_len);
+ uint8_t* ptr = hdr.getPayloadAppendPtr();
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ *(ptr + i) = pkt1[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ *(ptr + i + pkt1_len) = pkt2[i];
+ }
+
+ // copy packet 3
+ for (uint16_t i = 0; i < pkt3_len; i++) {
+ *(ptr + i + pkt1_len + pkt2_len) = pkt3[i];
+ }
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt4_len; i++) {
+ *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i];
+ }
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+
+ hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt3_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt3[i]);
+ }
+
+ hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt4_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt4[i]);
+ }
+}
+
+TEST_F(AggregatedPktHeaderTest, Defrag4Packets8bit) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17};
+ uint16_t pkt2_len = 7;
+
+ std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
+ uint16_t pkt3_len = 10;
+
+ std::vector<uint8_t> pkt4 = {100, 110};
+ uint16_t pkt4_len = 2;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt1_len, 4);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ hdr.addPacketToHeader(2, pkt3_len);
+ hdr.addPacketToHeader(3, pkt4_len);
+
+ uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 8; // 8 = aggr hdr
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ buf[i + offset] = pkt1[i];
+ }
+ offset += pkt1_len;
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ buf[i + offset] = pkt2[i];
+ }
+ offset += pkt2_len;
+
+ // copy packet 3
+ for (uint16_t i = 0; i < pkt3_len; i++) {
+ buf[i + offset] = pkt3[i];
+ }
+ offset += pkt3_len;
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt4_len; i++) {
+ buf[i + offset] = pkt4[i];
+ }
+
+ protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE);
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ uint8_t packet_number = hdr2.getNumberOfPackets();
+ EXPECT_EQ(packet_number, 4);
+
+ hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+
+ hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt3_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt3[i]);
+ }
+
+ hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt4_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt4[i]);
+ }
+}
+
+TEST_F(AggregatedPktHeaderTest, Defrag4Packets16bit) {
+ uint8_t buf[1500];
+
+ std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ uint16_t pkt1_len = 14;
+
+ std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17};
+ uint16_t pkt2_len = 7;
+
+ std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260
+ uint16_t pkt3_len = 260;
+
+ std::vector<uint8_t> pkt4 = {100, 110};
+ uint16_t pkt4_len = 2;
+
+ for (uint16_t i = 0; i < 1500; i++) {
+ buf[i] = 0;
+ }
+
+ // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header
+ protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE,
+ pkt3_len, 4);
+ hdr.addPacketToHeader(0, pkt1_len);
+ hdr.addPacketToHeader(1, pkt2_len);
+ hdr.addPacketToHeader(2, pkt3_len);
+ hdr.addPacketToHeader(3, pkt4_len);
+
+ uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 12; // 12 = aggr hdr
+
+ // copy packet 1
+ for (uint16_t i = 0; i < pkt1_len; i++) {
+ buf[i + offset] = pkt1[i];
+ }
+ offset += pkt1_len;
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt2_len; i++) {
+ buf[i + offset] = pkt2[i];
+ }
+ offset += pkt2_len;
+
+ // copy packet 3
+ for (uint16_t i = 0; i < pkt3_len; i++) {
+ buf[i + offset] = pkt3[i];
+ }
+ offset += pkt3_len;
+
+ // copy packet 2
+ for (uint16_t i = 0; i < pkt4_len; i++) {
+ buf[i + offset] = pkt4[i];
+ }
+
+ protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE);
+
+ uint8_t* pkt_ptr = nullptr;
+ uint16_t pkt_len = 0;
+
+ uint8_t packet_number = hdr2.getNumberOfPackets();
+ EXPECT_EQ(packet_number, 4);
+
+ hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt1_len);
+ for (uint16_t i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt1[i]);
+ }
+
+ hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt2_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt2[i]);
+ }
+
+ hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt3_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt3[i]);
+ }
+
+ hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len);
+ EXPECT_EQ(pkt_len, pkt4_len);
+ for (int i = 0; i < pkt_len; i++) {
+ EXPECT_EQ(*(pkt_ptr + i), pkt4[i]);
+ }
+}
+
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_auth.cc b/libtransport/src/test/test_auth.cc
new file mode 100644
index 000000000..5440d3741
--- /dev/null
+++ b/libtransport/src/test/test_auth.cc
@@ -0,0 +1,325 @@
+/*
+ * 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 <hicn/transport/auth/crypto_hash.h>
+#include <hicn/transport/auth/signer.h>
+#include <hicn/transport/auth/verifier.h>
+#include <hicn/transport/core/content_object.h>
+#include <openssl/rand.h>
+
+using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
+using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
+using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
+using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>;
+
+namespace transport {
+namespace auth {
+
+namespace {
+class AuthTest : public ::testing::Test {
+ protected:
+ const std::string PASSPHRASE = "hunter2";
+
+ AuthTest() = default;
+ ~AuthTest() {}
+ void SetUp() override {}
+ void TearDown() override {}
+};
+} // namespace
+
+TEST_F(AuthTest, VoidVerifier) {
+ // Create a content object
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH);
+
+ // Fill it with bogus data
+ uint8_t buffer[256] = {0};
+ packet.appendPayload(buffer, 256);
+
+ // Verify that VoidVerifier validates the packet
+ std::shared_ptr<Verifier> verifier = std::make_shared<VoidVerifier>();
+ EXPECT_EQ(verifier->verifyPacket(&packet), true);
+ EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT);
+}
+
+TEST_F(AuthTest, AsymmetricRSA) {
+ // Create the RSA keys
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free);
+ RSA_ptr rsa(RSA_new(), ::RSA_free);
+ BN_ptr pub_exp(BN_new(), ::BN_free);
+
+ BN_set_word(pub_exp.get(), RSA_F4);
+ if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL))
+ throw errors::RuntimeException("can't generate the key");
+ RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free);
+ RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free);
+ if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get()))
+ throw errors::RuntimeException("can't generate the key");
+ if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get()))
+ throw errors::RuntimeException("can't generate the key");
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::RSA_SHA256, privateKey, pubKey);
+
+ // Create a content object
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ signer->getSignatureSize());
+
+ // Fill it with bogus data
+ uint8_t buffer[256] = {0};
+ packet.appendPayload(buffer, 256);
+
+ // Sign the packet
+ signer->signPacket(&packet);
+
+ // Create the RSA verifier
+ std::shared_ptr<Verifier> verifier =
+ std::make_shared<AsymmetricVerifier>(pubKey);
+
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256);
+ EXPECT_EQ(signer->getSuite(), CryptoSuite::RSA_SHA256);
+ EXPECT_EQ(signer->getSignatureSize(), 256u);
+ EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT);
+}
+
+TEST_F(AuthTest, AsymmetricBufferRSA) {
+ // Create the RSA keys
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free);
+ RSA_ptr rsa(RSA_new(), ::RSA_free);
+ BN_ptr pub_exp(BN_new(), ::BN_free);
+
+ BN_set_word(pub_exp.get(), RSA_F4);
+ if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL))
+ throw errors::RuntimeException("can't generate the key");
+ RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free);
+ RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free);
+ if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get()))
+ throw errors::RuntimeException("can't generate the key");
+ if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get()))
+ throw errors::RuntimeException("can't generate the key");
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::RSA_SHA256, privateKey, pubKey);
+
+ std::string payload = "bonjour";
+
+ std::vector<uint8_t> buffer(payload.begin(), payload.end());
+ signer->signBuffer(buffer);
+ utils::MemBuf::Ptr sig = signer->getSignature();
+
+ std::shared_ptr<AsymmetricVerifier> verif =
+ std::make_shared<AsymmetricVerifier>(pubKey);
+ bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256);
+ EXPECT_EQ(res, true);
+}
+
+TEST_F(AuthTest, AsymmetricBufferDSA) {
+ // Create the DSA keys
+
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+
+ DSA_ptr dsa(DSA_new(), ::DSA_free);
+ unsigned char buf[32];
+ if (RAND_bytes(buf, sizeof(buf)) != 1) {
+ throw errors::RuntimeException("can't generate the key");
+ }
+ if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL,
+ NULL) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (DSA_generate_key(dsa.get()) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()))
+ throw errors::RuntimeException("can't generate the key");
+
+ std::shared_ptr<X509> cert(X509_new(), ::X509_free);
+ X509_set_pubkey(cert.get(), privateKey.get());
+ std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free);
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::DSA_SHA256, privateKey, pubKey);
+
+ std::string payload = "bonjour";
+
+ std::vector<uint8_t> buffer(payload.begin(), payload.end());
+ signer->signBuffer(buffer);
+ utils::MemBuf::Ptr sig = signer->getSignature();
+
+ std::shared_ptr<AsymmetricVerifier> verif =
+ std::make_shared<AsymmetricVerifier>(pubKey);
+ bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256);
+ EXPECT_EQ(res, true);
+}
+
+TEST_F(AuthTest, AsymmetricVerifierDSA) {
+ // Create the DSA keys
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+
+ DSA_ptr dsa(DSA_new(), ::DSA_free);
+ unsigned char buf[32];
+ if (RAND_bytes(buf, sizeof(buf)) != 1) {
+ throw errors::RuntimeException("can't generate the key");
+ }
+ if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL,
+ NULL) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (DSA_generate_key(dsa.get()) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1)
+ throw errors::RuntimeException("can't generate the key");
+ if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()))
+ throw errors::RuntimeException("can't generate the key");
+
+ std::shared_ptr<X509> cert(X509_new(), ::X509_free);
+ X509_set_pubkey(cert.get(), privateKey.get());
+ std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free);
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::DSA_SHA256, privateKey, pubKey);
+
+ // Create a content object
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ signer->getSignatureSize());
+
+ // Fill it with bogus data
+ uint8_t buffer[256] = {0};
+ packet.appendPayload(buffer, 256);
+ // this test has to be done before the signature is compute
+ // EXPECT_EQ(signer->getSignatureSize(), 256u);
+ signer->signPacket(&packet);
+ std::shared_ptr<Verifier> verifier =
+ std::make_shared<AsymmetricVerifier>(cert);
+
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256);
+ EXPECT_EQ(signer->getSuite(), CryptoSuite::DSA_SHA256);
+ EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT);
+}
+
+TEST_F(AuthTest, AsymmetricBufferECDSA) {
+ // Create the ECDSA keys
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free);
+ EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free);
+ EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free);
+ EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_generate_key(ec_priv.get()) == 0)
+ throw errors::RuntimeException("can't generate the ecdsa key");
+ if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get()))
+ throw errors::RuntimeException("can't generate the key");
+ EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get()));
+ EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get()));
+ if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get()))
+ throw errors::RuntimeException("can't generate the key");
+
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::ECDSA_SHA256, privateKey, pubKey);
+
+ std::string payload = "bonjour";
+
+ std::vector<uint8_t> buffer(payload.begin(), payload.end());
+ signer->signBuffer(buffer);
+ utils::MemBuf::Ptr sig = signer->getSignature();
+
+ std::shared_ptr<AsymmetricVerifier> verif =
+ std::make_shared<AsymmetricVerifier>(pubKey);
+ bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256);
+ EXPECT_EQ(res, true);
+} // namespace auth
+
+TEST_F(AuthTest, AsymmetricVerifierECDSA) {
+ // Create the ECDSA keys
+ std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free);
+ EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free);
+ EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free);
+ EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_generate_key(ec_priv.get()) == 0)
+ throw errors::RuntimeException("can't generate the ecdsa key");
+ if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get()))
+ throw errors::RuntimeException("can't generate the key");
+ EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get()));
+ EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get()));
+ if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get()))
+ throw errors::RuntimeException("can't generate the key");
+
+ std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>(
+ CryptoSuite::ECDSA_SHA256, privateKey, pubKey);
+
+ std::shared_ptr<AsymmetricVerifier> verifier =
+ std::make_shared<AsymmetricVerifier>(pubKey);
+ for (int i = 0; i < 100; i++) {
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ signer->getSignatureSize());
+
+ // Fill it with bogus data
+ uint8_t buffer[256] = {0};
+ packet.appendPayload(buffer, 256);
+ signer->signPacket(&packet);
+
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256);
+ EXPECT_EQ(signer->getSuite(), CryptoSuite::ECDSA_SHA256);
+ EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT);
+ }
+}
+
+TEST_F(AuthTest, HMACbuffer) {
+ // Create the HMAC signer from a passphrase
+ std::shared_ptr<Signer> signer =
+ std::make_shared<SymmetricSigner>(CryptoSuite::HMAC_SHA256, PASSPHRASE);
+
+ // Create a content object
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ signer->getSignatureSize());
+
+ std::string payload = "bonjour";
+ std::vector<uint8_t> buffer(payload.begin(), payload.end());
+ signer->signBuffer(buffer);
+ utils::MemBuf::Ptr sig = signer->getSignature();
+ SymmetricVerifier hmac(PASSPHRASE);
+ bool res = hmac.verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256);
+ EXPECT_EQ(res, true);
+}
+
+TEST_F(AuthTest, HMACVerifier) {
+ // Create the HMAC signer from a passphrase
+ std::shared_ptr<SymmetricSigner> signer =
+ std::make_shared<SymmetricSigner>(CryptoSuite::HMAC_SHA256, PASSPHRASE);
+
+ // Create a content object
+ core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ signer->getSignatureSize());
+
+ // Fill it with bogus data
+ uint8_t buffer[256] = {0};
+ packet.appendPayload(buffer, 256);
+
+ // Sign the packet
+ signer->signPacket(&packet);
+
+ // Create the HMAC verifier
+ std::shared_ptr<Verifier> verifier =
+ std::make_shared<SymmetricVerifier>(PASSPHRASE);
+
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256);
+ EXPECT_EQ(signer->getSuite(), CryptoSuite::HMAC_SHA256);
+ EXPECT_EQ(signer->getSignatureSize(), 32u);
+ EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT);
+}
+
+} // namespace auth
+} // namespace transport
diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc
new file mode 100644
index 000000000..d7378fc72
--- /dev/null
+++ b/libtransport/src/test/test_consumer_producer_rtc.cc
@@ -0,0 +1,172 @@
+/*
+ * 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 <hicn/transport/core/asio_wrapper.h>
+#include <hicn/transport/interfaces/global_conf_interface.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_options_keys.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+
+namespace transport {
+namespace interface {
+
+namespace {
+
+class IoModuleInit {
+ public:
+ IoModuleInit() {
+ global_config::IoModuleConfiguration config;
+ config.name = "forwarder_module";
+ config.set();
+ }
+};
+
+static IoModuleInit init;
+
+class ConsumerProducerTest : public ::testing::Test,
+ public ConsumerSocket::ReadCallback {
+ static const constexpr char prefix[] = "b001::1/128";
+ static const constexpr char name[] = "b001::1";
+ static const constexpr double prod_rate = 1.0e6;
+ static const constexpr size_t payload_size = 1200;
+ static constexpr std::size_t receive_buffer_size = 1500;
+ static const constexpr double prod_interval_microseconds =
+ double(payload_size) * 8 * 1e6 / prod_rate;
+
+ public:
+ ConsumerProducerTest()
+ : io_service_(),
+ rtc_timer_(io_service_),
+ stop_timer_(io_service_),
+ consumer_(TransportProtocolAlgorithms::RTC),
+ producer_(ProductionProtocolAlgorithms::RTC_PROD),
+ producer_prefix_(prefix),
+ consumer_name_(name),
+ packets_sent_(0),
+ packets_received_(0) {}
+
+ virtual ~ConsumerProducerTest() {
+ // 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() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+
+ auto ret = consumer_.setSocketOption(
+ ConsumerCallbacksOptions::READ_CALLBACK, this);
+ ASSERT_EQ(ret, SOCKET_OPTION_SET);
+
+ consumer_.connect();
+ producer_.registerPrefix(producer_prefix_);
+ producer_.connect();
+ producer_.start();
+ }
+
+ virtual void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+
+ void setTimer() {
+ using namespace std::chrono;
+ rtc_timer_.expires_from_now(
+ microseconds(unsigned(prod_interval_microseconds)));
+ rtc_timer_.async_wait(std::bind(&ConsumerProducerTest::produceRTCPacket,
+ this, std::placeholders::_1));
+ }
+
+ void setStopTimer() {
+ using namespace std::chrono;
+ stop_timer_.expires_from_now(seconds(unsigned(10)));
+ stop_timer_.async_wait(
+ std::bind(&ConsumerProducerTest::stop, this, std::placeholders::_1));
+ }
+
+ void produceRTCPacket(const std::error_code &ec) {
+ if (ec) {
+ io_service_.stop();
+ }
+
+ producer_.produceDatagram(consumer_name_, payload_, payload_size);
+ packets_sent_++;
+ setTimer();
+ }
+
+ void stop(const std::error_code &ec) {
+ rtc_timer_.cancel();
+ producer_.stop();
+ consumer_.stop();
+ }
+
+ // Consumer callback
+ bool isBufferMovable() noexcept override { return false; }
+
+ void getReadBuffer(uint8_t **application_buffer,
+ size_t *max_length) override {
+ *application_buffer = receive_buffer_;
+ *max_length = receive_buffer_size;
+ }
+
+ void readDataAvailable(std::size_t length) noexcept override {
+ packets_received_++;
+ }
+
+ size_t maxBufferSize() const override { return receive_buffer_size; }
+
+ void readError(const std::error_code &ec) noexcept override {
+ io_service_.stop();
+ FAIL() << "Error while reading from RTC socket";
+ }
+
+ void readSuccess(std::size_t total_size) noexcept override {}
+
+ asio::io_service io_service_;
+ asio::steady_timer rtc_timer_;
+ asio::steady_timer stop_timer_;
+ ConsumerSocket consumer_;
+ ProducerSocket producer_;
+ core::Prefix producer_prefix_;
+ core::Name consumer_name_;
+ uint8_t payload_[payload_size];
+ uint8_t receive_buffer_[payload_size];
+
+ uint64_t packets_sent_;
+ uint64_t packets_received_;
+};
+
+const char ConsumerProducerTest::prefix[];
+const char ConsumerProducerTest::name[];
+
+} // namespace
+
+TEST_F(ConsumerProducerTest, EndToEnd) {
+ produceRTCPacket(std::error_code());
+ consumer_.consume(consumer_name_);
+ setStopTimer();
+
+ io_service_.run();
+
+ std::cout << "Packet received: " << packets_received_ << std::endl;
+ std::cout << "Packet sent: " << packets_sent_ << std::endl;
+}
+
+} // namespace interface
+
+} // namespace transport
diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc
new file mode 100644
index 000000000..99c71a56c
--- /dev/null
+++ b/libtransport/src/test/test_core_manifest.cc
@@ -0,0 +1,312 @@
+/*
+ * 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 <core/manifest.h>
+#include <core/manifest_format_fixed.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/auth/crypto_hash.h>
+#include <hicn/transport/auth/signer.h>
+#include <hicn/transport/auth/verifier.h>
+#include <test/packet_samples.h>
+
+#include <climits>
+#include <random>
+#include <vector>
+
+namespace transport {
+
+namespace core {
+
+namespace {
+// The fixture for testing class Foo.
+class ManifestTest : public ::testing::Test {
+ protected:
+ using ContentObjectManifest = Manifest<Fixed>;
+
+ ManifestTest()
+ : format_(HICN_PACKET_FORMAT_IPV6_TCP_AH),
+ name_("b001::123|321"),
+ signature_size_(0) {
+ manifest_ = ContentObjectManifest::createContentManifest(format_, name_,
+ signature_size_);
+ }
+
+ virtual ~ManifestTest() {
+ // 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).
+ }
+
+ Packet::Format format_;
+ Name name_;
+ std::size_t signature_size_;
+ std::shared_ptr<ContentObjectManifest> manifest_;
+ std::vector<uint8_t> manifest_payload_ = {
+ 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00,
+ // 0x00, 0x45, 0xa3,
+ // 0xd1, 0xf2, 0x2b,
+ // 0x94, 0x41, 0x22,
+ // 0xc9, 0x00, 0x00,
+ // 0x00, 0x44, 0xa3,
+ // 0xd1, 0xf2, 0x2b,
+ // 0x94, 0x41, 0x22,
+ // 0xc8
+ };
+};
+
+} // namespace
+
+TEST_F(ManifestTest, ManifestConstructor) {
+ // Create content object with manifest in payload
+ ContentObject::Ptr co =
+ core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ format_, signature_size_);
+ co->setName(name_);
+ co->appendPayload(manifest_payload_.data(), manifest_payload_.size());
+
+ uint8_t buffer[256] = {0};
+ co->appendPayload(buffer, 256);
+
+ // Copy packet payload
+ uint8_t packet[1500];
+ auto length = co->getPayload()->length();
+ std::memcpy(packet, co->getPayload()->data(), length);
+
+ // Create manifest
+ ContentObjectManifest manifest(co);
+
+ // Check manifest payload is exactly the same of content object
+ ASSERT_EQ(length, manifest.getPacket()->getPayload()->length());
+ auto ret =
+ std::memcmp(packet, manifest.getPacket()->getPayload()->data(), length);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(ManifestTest, SetManifestType) {
+ manifest_->Encoder::clear();
+
+ ManifestType type1 = ManifestType::INLINE_MANIFEST;
+ ManifestType type2 = ManifestType::FLIC_MANIFEST;
+
+ manifest_->setType(type1);
+ ManifestType type_returned1 = manifest_->getType();
+
+ manifest_->Encoder::clear();
+
+ manifest_->setType(type2);
+ ManifestType type_returned2 = manifest_->getType();
+
+ ASSERT_EQ(type1, type_returned1);
+ ASSERT_EQ(type2, type_returned2);
+}
+
+TEST_F(ManifestTest, SetMaxCapacity) {
+ manifest_->Encoder::clear();
+
+ uint8_t max_capacity1 = 0;
+ uint8_t max_capacity2 = 20;
+
+ manifest_->setMaxCapacity(max_capacity1);
+ uint8_t max_capacity_returned1 = manifest_->getMaxCapacity();
+
+ manifest_->Encoder::clear();
+
+ manifest_->setMaxCapacity(max_capacity2);
+ uint8_t max_capacity_returned2 = manifest_->getMaxCapacity();
+
+ ASSERT_EQ(max_capacity1, max_capacity_returned1);
+ ASSERT_EQ(max_capacity2, max_capacity_returned2);
+}
+
+TEST_F(ManifestTest, SetHashAlgorithm) {
+ manifest_->Encoder::clear();
+
+ auth::CryptoHashType hash1 = auth::CryptoHashType::SHA256;
+ auth::CryptoHashType hash2 = auth::CryptoHashType::SHA512;
+ auth::CryptoHashType hash3 = auth::CryptoHashType::BLAKE2B512;
+
+ manifest_->setHashAlgorithm(hash1);
+ auto type_returned1 = manifest_->getHashAlgorithm();
+
+ manifest_->Encoder::clear();
+
+ manifest_->setHashAlgorithm(hash2);
+ auto type_returned2 = manifest_->getHashAlgorithm();
+
+ manifest_->Encoder::clear();
+
+ manifest_->setHashAlgorithm(hash3);
+ auto type_returned3 = manifest_->getHashAlgorithm();
+
+ ASSERT_EQ(hash1, type_returned1);
+ ASSERT_EQ(hash2, type_returned2);
+ ASSERT_EQ(hash3, type_returned3);
+}
+
+TEST_F(ManifestTest, SetLastManifest) {
+ manifest_->Encoder::clear();
+
+ manifest_->setIsLast(true);
+ bool is_last = manifest_->getIsLast();
+
+ ASSERT_TRUE(is_last);
+}
+
+TEST_F(ManifestTest, SetBaseName) {
+ manifest_->Encoder::clear();
+
+ core::Name base_name("b001::dead");
+
+ manifest_->setBaseName(base_name);
+ core::Name ret_name = manifest_->getBaseName();
+
+ ASSERT_EQ(base_name, ret_name);
+}
+
+TEST_F(ManifestTest, setParamsBytestream) {
+ manifest_->Encoder::clear();
+
+ ParamsBytestream params{
+ .final_segment = 0x0a,
+ };
+
+ manifest_->setParamsBytestream(params);
+ auth::CryptoHash hash(auth::CryptoHashType::SHA256);
+ hash.computeDigest({0x01, 0x02, 0x03, 0x04});
+ manifest_->addEntry(1, hash);
+
+ manifest_->encode();
+ manifest_->decode();
+
+ auto transport_type_returned = manifest_->getTransportType();
+ auto params_returned = manifest_->getParamsBytestream();
+
+ ASSERT_EQ(interface::ProductionProtocolAlgorithms::BYTE_STREAM,
+ transport_type_returned);
+ ASSERT_EQ(params, params_returned);
+}
+
+TEST_F(ManifestTest, SetParamsRTC) {
+ manifest_->Encoder::clear();
+
+ ParamsRTC params{
+ .timestamp = 0x0a,
+ .prod_rate = 0x0b,
+ .prod_seg = 0x0c,
+ .fec_type = protocol::fec::FECType::UNKNOWN,
+ };
+
+ manifest_->setParamsRTC(params);
+ auth::CryptoHash hash(auth::CryptoHashType::SHA256);
+ hash.computeDigest({0x01, 0x02, 0x03, 0x04});
+ manifest_->addEntry(1, hash);
+
+ manifest_->encode();
+ manifest_->decode();
+
+ auto transport_type_returned = manifest_->getTransportType();
+ auto params_returned = manifest_->getParamsRTC();
+
+ ASSERT_EQ(interface::ProductionProtocolAlgorithms::RTC_PROD,
+ transport_type_returned);
+ ASSERT_EQ(params, params_returned);
+}
+
+TEST_F(ManifestTest, SignManifest) {
+ auto signer = std::make_shared<auth::SymmetricSigner>(
+ auth::CryptoSuite::HMAC_SHA256, "hunter2");
+ auto verifier = std::make_shared<auth::SymmetricVerifier>("hunter2");
+
+ // Instantiate manifest
+ uint8_t max_capacity = 30;
+ std::shared_ptr<ContentObjectManifest> manifest =
+ ContentObjectManifest::createContentManifest(
+ format_, name_, signer->getSignatureFieldSize());
+ manifest->setHeaders(ManifestType::INLINE_MANIFEST, max_capacity,
+ signer->getHashType(), false /* is_last */, name_);
+
+ // Add manifest entry
+ auth::CryptoHash hash(signer->getHashType());
+ hash.computeDigest({0x01, 0x02, 0x03, 0x04});
+ manifest->addEntry(1, hash);
+
+ // Encode manifest
+ manifest->encode();
+ auto manifest_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest->getPacket());
+
+ // Sign manifest
+ signer->signPacket(manifest_co.get());
+
+ // Check size
+ ASSERT_EQ(manifest_co->payloadSize(), manifest->Encoder::manifestSize());
+ ASSERT_EQ(manifest_co->length(),
+ manifest_co->headerSize() + manifest_co->payloadSize());
+
+ // Verify manifest
+ auth::VerificationPolicy policy = verifier->verifyPackets(manifest_co.get());
+ ASSERT_EQ(auth::VerificationPolicy::ACCEPT, policy);
+}
+
+TEST_F(ManifestTest, SetSuffixList) {
+ manifest_->Encoder::clear();
+
+ using random_bytes_engine =
+ std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
+ unsigned char>;
+ random_bytes_engine rbe;
+
+ std::default_random_engine eng((std::random_device())());
+ std::uniform_int_distribution<uint64_t> idis(
+ 0, std::numeric_limits<uint32_t>::max());
+
+ auto entries = new std::pair<uint32_t, auth::CryptoHash>[3];
+ uint32_t suffixes[3];
+ std::vector<unsigned char> data[3];
+
+ for (int i = 0; i < 3; i++) {
+ data[i].resize(32);
+ std::generate(std::begin(data[i]), std::end(data[i]), std::ref(rbe));
+ suffixes[i] = idis(eng);
+ entries[i] = std::make_pair(suffixes[i],
+ auth::CryptoHash(data[i].data(), data[i].size(),
+ auth::CryptoHashType::SHA256));
+ manifest_->addEntry(entries[i].first, entries[i].second);
+ }
+
+ core::Name base_name("b001::dead");
+ manifest_->setBaseName(base_name);
+
+ core::Name ret_name = manifest_->getBaseName();
+ ASSERT_EQ(base_name, ret_name);
+
+ delete[] entries;
+}
+
+} // namespace core
+
+} // namespace transport
diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc
new file mode 100644
index 000000000..324250717
--- /dev/null
+++ b/libtransport/src/test/test_event_thread.cc
@@ -0,0 +1,95 @@
+/*
+ * 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 <hicn/transport/utils/chrono_typedefs.h>
+#include <hicn/transport/utils/event_thread.h>
+
+#include <cmath>
+
+namespace utils {
+
+namespace {
+
+class EventThreadTest : public ::testing::Test {
+ protected:
+ EventThreadTest() : event_thread_() {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~EventThreadTest() {
+ // 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).
+ }
+
+ utils::EventThread event_thread_;
+};
+
+inline double average(const unsigned long samples[], int size) {
+ double sum = 0;
+
+ for (int i = 0; i < size; i++) {
+ sum += samples[i];
+ }
+
+ return sum / size;
+}
+
+inline double stdDeviation(const unsigned long samples[], int size) {
+ double avg = average(samples, size);
+ double var = 0;
+
+ for (int i = 0; i < size; i++) {
+ var += (samples[i] - avg) * (samples[i] - avg);
+ }
+
+ return sqrt(var / size);
+}
+
+} // namespace
+
+TEST_F(EventThreadTest, DISABLED_SchedulingDelay) {
+ // using namespace std::chrono;
+ // const size_t size = 1000000;
+ // std::vector<unsigned long> samples(size);
+
+ // for (unsigned int i = 0; i < size; i++) {
+ // event_thread_.add([t0, &samples, i]() {
+ // });
+ // }
+
+ // event_thread_.stop();
+
+ // auto avg = average(&samples[0], size);
+ // auto sd = stdDeviation(&samples[0], size);
+ // (void)sd;
+
+ // // Expect average to be less that 1 ms
+ // EXPECT_LT(avg, 1000000);
+}
+
+} // namespace utils
diff --git a/libtransport/src/test/test_fec_base_rely.cc b/libtransport/src/test/test_fec_base_rely.cc
new file mode 100644
index 000000000..2ee00e25e
--- /dev/null
+++ b/libtransport/src/test/test_fec_base_rely.cc
@@ -0,0 +1,454 @@
+/*
+ * 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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <protocols/fec_base.h>
+#include <protocols/fec_utils.h>
+#include <protocols/rtc/rtc_consts.h>
+
+#include <algorithm>
+#include <iostream>
+#include <queue>
+#include <random>
+
+namespace transport {
+namespace protocol {
+
+class PacketFactory {
+ public:
+ PacketFactory(){};
+
+ ~PacketFactory(){};
+
+ std::shared_ptr<transport::core::ContentObject> createData(
+ core::Name &name, uint32_t suffix, uint32_t payload_size,
+ uint32_t payload_filler) {
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ // create payload
+ auto buff = packet_manager.getMemBuf();
+ buff->append(payload_size);
+ std::fill(buff->writableData(), buff->writableTail(), payload_filler);
+
+ // create data packet
+ auto data = packet_manager.getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ struct rtc::data_packet_t header;
+ header.setTimestamp(1000);
+ header.setProductionRate(1);
+ data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE);
+ data->appendPayload(buff->data(), buff->length());
+ data->setName(name.setSuffix(suffix));
+ data->setLifetime(500);
+ data->setPathLabel(12);
+
+ return data;
+ }
+
+ std::shared_ptr<transport::core::ContentObject> createData(
+ core::Name &name, uint32_t suffix, fec::buffer payload) {
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ auto data = packet_manager.getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ struct rtc::data_packet_t header;
+ header.setTimestamp(1000);
+ header.setProductionRate(1);
+ data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE);
+ data->appendPayload(payload->data(), payload->length());
+ data->setName(name.setSuffix(suffix));
+ data->setLifetime(500);
+ data->setPathLabel(12);
+
+ return data;
+ }
+};
+
+class Encoder {
+ public:
+ Encoder(std::string fec_str) {
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str());
+ if (fec_type_ == fec::FECType::UNKNOWN)
+ std::cout << "x" << fec_str << "x" << std::endl;
+ encoder_ = fec::FECUtils::getEncoder(fec_type_, 1);
+ encoder_->setFECCallback(
+ std::bind(&Encoder::onFecPackets, this, std::placeholders::_1));
+ encoder_->setBufferCallback(
+ std::bind(&Encoder::getBuffer, this, std::placeholders::_1));
+ };
+
+ ~Encoder(){};
+
+ void onFecPackets(fec::BufferArray &packets) {
+ for (auto &packet : packets) {
+ fec_packets_.push(packet.getBuffer());
+ }
+ }
+
+ fec::buffer getBuffer(std::size_t size) {
+ auto ret = core::PacketManager<>::getInstance()
+ .getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ ret->updateLength(rtc::DATA_HEADER_SIZE + size);
+ ret->append(rtc::DATA_HEADER_SIZE + size);
+ ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ return ret;
+ }
+
+ void onPacketProduced(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata) {
+ encoder_->onPacketProduced(content_object, offset, metadata);
+ }
+
+ public:
+ std::queue<fec::buffer> fec_packets_;
+
+ private:
+ std::unique_ptr<fec::ProducerFEC> encoder_;
+ fec::FECType fec_type_;
+};
+
+class Decoder {
+ public:
+ Decoder(std::string fec_str) {
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str());
+ decoder_ = fec::FECUtils::getDecoder(fec_type_, 1);
+ decoder_->setFECCallback(
+ std::bind(&Decoder::onFecPackets, this, std::placeholders::_1));
+ decoder_->setBufferCallback(fec::FECBase::BufferRequested(0));
+ };
+
+ ~Decoder(){};
+
+ void onFecPackets(fec::BufferArray &packets) {
+ for (auto &packet : packets) {
+ hicn_packet_dump(packet.getBuffer()->data(),
+ packet.getBuffer()->length());
+ recovered_packets_.push(packet.getBuffer());
+ }
+ }
+
+ void onPacketReceived(core::ContentObject &content_object, uint32_t offset) {
+ decoder_->onDataPacket(content_object, offset);
+ }
+
+ public:
+ std::queue<fec::buffer> recovered_packets_;
+
+ private:
+ std::unique_ptr<fec::ConsumerFEC> decoder_;
+ fec::FECType fec_type_;
+};
+
+TEST(FECtestRely, RelyTestInOrder1) {
+ // use Rely k = 2 N = 6
+ std::string fec_str = "Rely_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ // we cannot use the original data 1 to check it we recovered the packet
+ // correclty because Rely modifies the packet so here we create a copy
+ auto data1_copy = pf.createData(name, 1, 50, 1);
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decode in order, data 1 is lost
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // check recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front());
+
+ bool eq_len = (data1_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data1_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRely, RelyTestInOrder2) {
+ // use Rely k = 2 N = 6
+ std::string fec_str = "Rely_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ auto data2_copy = pf.createData(name, 2, 45, 2);
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decode in order, data 2 is lost
+ decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // check recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ decoder.recovered_packets_.pop(); // pop data 1
+ auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front());
+
+ bool eq_len = (data2_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data2_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+#if 0
+TEST(FECtestRely, RelyTestOutOfOrder1) {
+ //use Rely k = 2 N = 6
+ std::string fec_str = "Rely_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ auto data1_copy = pf.createData(name, 1, 50, 1);
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+
+ std::cout << "dump packet 2" << std::endl;
+ hicn_packet_dump(data2->data(), data2->length());
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ std::cout << "dump packet 2" << std::endl;
+ hicn_packet_dump(data2->data(), data2->length());
+
+ std::cout << "dump packet 3" << std::endl;
+ hicn_packet_dump(data3->data(), data3->length());
+
+ // decoding ooo packets. data 1 is lost.
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t) 2);
+ auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front());
+ bool eq_len = (data1_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if(eq_len)
+ ret = memcmp(data1_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int) 0);
+}
+#endif
+
+TEST(FECtestRely, RelyTestOutOfOrder2) {
+ // use Rely k = 2 N = 6
+ std::string fec_str = "Rely_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ auto data2_copy = pf.createData(name, 2, 45, 2);
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decoding ooo packets. data 2 is lost.
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ decoder.recovered_packets_.pop(); // pop data 1
+ auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front());
+ bool eq_len = (data2_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data2_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRely, RelyTestLargerBlocks) {
+ // use Rely k = 4 N = 7
+ std::string fec_str = "Rely_K4_N7";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+
+ auto data3 = pf.createData(name, 3, 12, 3);
+ auto data3_copy = pf.createData(name, 3, 12, 3);
+
+ auto data4 = pf.createData(name, 4, 20, 4);
+ auto data4_copy = pf.createData(name, 4, 20, 4);
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data5 = pf.createData(name, 5, encoder.fec_packets_.front());
+ encoder.fec_packets_.pop(); // pop 5
+ encoder.fec_packets_.pop(); // pop 6
+ auto data7 = pf.createData(name, 7, encoder.fec_packets_.front());
+
+ // decoding packets: lost data 3 and data 4
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4);
+ decoder.recovered_packets_.pop(); // pop data 1
+ decoder.recovered_packets_.pop(); // pop data 2
+ auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front());
+ decoder.recovered_packets_.pop();
+ auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front());
+
+ bool eq_len = (data3_copy->length() == recovered3->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data3_copy->data(), recovered3->data(), recovered3->length());
+ EXPECT_EQ(ret, (int)0);
+
+ eq_len = (data4_copy->length() == recovered4->length());
+ EXPECT_TRUE(eq_len);
+ ret = -1;
+ if (eq_len)
+ ret = memcmp(data4_copy->data(), recovered4->data(), recovered4->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRely, RelyTestK1_N3) {
+ // use Rely k = 1 N = 3
+ std::string fec_str = "Rely_K1_N3";
+ Encoder encoder(fec_str);
+ Decoder decoder1(fec_str); // recv 2
+ Decoder decoder2(fec_str); // recv 3
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ auto data1_copy = pf.createData(name, 1, 50, 1);
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packets
+ EXPECT_TRUE(encoder.fec_packets_.size() == 2);
+ auto data2 = pf.createData(name, 2, encoder.fec_packets_.front());
+ encoder.fec_packets_.pop(); // pop 2
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // test1: recv data 2
+ decoder1.onPacketReceived(*data2,
+ data2->headerSize() + rtc::DATA_HEADER_SIZE);
+ EXPECT_EQ(decoder1.recovered_packets_.size(), (size_t)1);
+
+ auto recovered = pf.createData(name, 1, decoder1.recovered_packets_.front());
+ bool eq_len = (data1_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data1_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+
+ // test2: recv data 3
+ decoder2.onPacketReceived(*data3,
+ data3->headerSize() + rtc::DATA_HEADER_SIZE);
+ EXPECT_EQ(decoder2.recovered_packets_.size(), (size_t)1);
+
+ recovered = pf.createData(name, 1, decoder2.recovered_packets_.front());
+ eq_len = (data1_copy->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ ret = -1;
+ if (eq_len)
+ ret = memcmp(data1_copy->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_fec_base_rs.cc b/libtransport/src/test/test_fec_base_rs.cc
new file mode 100644
index 000000000..549bb3f08
--- /dev/null
+++ b/libtransport/src/test/test_fec_base_rs.cc
@@ -0,0 +1,413 @@
+/*
+ * 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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <protocols/fec_base.h>
+#include <protocols/fec_utils.h>
+#include <protocols/rtc/rtc_consts.h>
+
+#include <algorithm>
+#include <iostream>
+#include <queue>
+#include <random>
+
+namespace transport {
+namespace protocol {
+
+class PacketFactory {
+ public:
+ PacketFactory(){};
+
+ ~PacketFactory(){};
+
+ std::shared_ptr<transport::core::ContentObject> createData(
+ core::Name &name, uint32_t suffix, uint32_t payload_size,
+ uint32_t payload_filler) {
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ // create payload
+ auto buff = packet_manager.getMemBuf();
+ buff->append(payload_size);
+ std::fill(buff->writableData(), buff->writableTail(), payload_filler);
+
+ // create data packet
+ auto data = packet_manager.getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ struct rtc::data_packet_t header;
+ header.setTimestamp(1000);
+ header.setProductionRate(1);
+ data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE);
+ data->appendPayload(buff->data(), buff->length());
+ data->setName(name.setSuffix(suffix));
+ data->setLifetime(500);
+ data->setPathLabel(12);
+
+ return data;
+ }
+
+ std::shared_ptr<transport::core::ContentObject> createData(
+ core::Name &name, uint32_t suffix, fec::buffer payload) {
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ auto data = packet_manager.getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ struct rtc::data_packet_t header;
+ header.setTimestamp(1000);
+ header.setProductionRate(1);
+ data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE);
+ data->appendPayload(payload->data(), payload->length());
+ data->setName(name.setSuffix(suffix));
+ data->setLifetime(500);
+ data->setPathLabel(12);
+
+ return data;
+ }
+};
+
+class Encoder {
+ public:
+ Encoder(std::string fec_str) {
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str());
+ encoder_ = fec::FECUtils::getEncoder(fec_type_, 1);
+ encoder_->setFECCallback(
+ std::bind(&Encoder::onFecPackets, this, std::placeholders::_1));
+ encoder_->setBufferCallback(
+ std::bind(&Encoder::getBuffer, this, std::placeholders::_1));
+ };
+
+ ~Encoder(){};
+
+ void onFecPackets(fec::BufferArray &packets) {
+ for (auto &packet : packets) {
+ fec_packets_.push(packet.getBuffer());
+ }
+ }
+
+ fec::buffer getBuffer(std::size_t size) {
+ auto ret = core::PacketManager<>::getInstance()
+ .getPacket<transport::core::ContentObject>(
+ HICN_PACKET_FORMAT_IPV6_TCP, 0);
+ ret->updateLength(rtc::DATA_HEADER_SIZE + size);
+ ret->append(rtc::DATA_HEADER_SIZE + size);
+ ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ return ret;
+ }
+
+ void onPacketProduced(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata) {
+ encoder_->onPacketProduced(content_object, offset, metadata);
+ }
+
+ public:
+ std::queue<fec::buffer> fec_packets_;
+
+ private:
+ std::unique_ptr<fec::ProducerFEC> encoder_;
+ fec::FECType fec_type_;
+};
+
+class Decoder {
+ public:
+ Decoder(std::string fec_str) {
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str());
+ decoder_ = fec::FECUtils::getDecoder(fec_type_, 1);
+ decoder_->setFECCallback(
+ std::bind(&Decoder::onFecPackets, this, std::placeholders::_1));
+ decoder_->setBufferCallback(fec::FECBase::BufferRequested(0));
+ };
+
+ ~Decoder(){};
+
+ void onFecPackets(fec::BufferArray &packets) {
+ for (auto &packet : packets) {
+ recovered_packets_.push(packet.getBuffer());
+ }
+ }
+
+ void onPacketReceived(core::ContentObject &content_object, uint32_t offset) {
+ decoder_->onDataPacket(content_object, offset);
+ }
+
+ public:
+ std::queue<fec::buffer> recovered_packets_;
+
+ private:
+ std::unique_ptr<fec::ConsumerFEC> decoder_;
+ fec::FECType fec_type_;
+};
+
+TEST(FECtestRS, RSTestInOrder1) {
+ // use RS k = 2 N = 6
+ std::string fec_str = "RS_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ const uint8_t *data1_ptr = data1->data();
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ const uint8_t *data2_ptr = data2->data();
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decode in order, data 1 is lost
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // test payload pointers off the original packets
+ EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr);
+ EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr);
+
+ // check recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front());
+ bool eq_len = (data1->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data1->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRS, RSTestInOrder2) {
+ // use RS k = 2 N = 6
+ std::string fec_str = "RS_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ const uint8_t *data1_ptr = data1->data();
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ const uint8_t *data2_ptr = data2->data();
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decode in order, data 2 is lost
+ decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // test payload pointers off the original packets
+ EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr);
+ EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr);
+
+ // check recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ decoder.recovered_packets_.pop(); // pop data packet 1
+ auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front());
+
+ bool eq_len = (data2->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data2->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRS, RSTestOutOfOrder1) {
+ // use RS k = 2 N = 6
+ std::string fec_str = "RS_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ const uint8_t *data1_ptr = data1->data();
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ const uint8_t *data2_ptr = data2->data();
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decoding ooo packets. data 1 is lost.
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // test payload pointers off the original packets
+ EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr);
+ EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front());
+ bool eq_len = (data1->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data1->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRS, RSTestOutOfOrder2) {
+ // use RS k = 2 N = 6
+ std::string fec_str = "RS_K2_N6";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ const uint8_t *data1_ptr = data1->data();
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ const uint8_t *data2_ptr = data2->data();
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data3 = pf.createData(name, 3, encoder.fec_packets_.front());
+
+ // decoding ooo packets. data 2 is lost.
+ decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // test payload pointers off the original packets
+ EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr);
+ EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2);
+ decoder.recovered_packets_.pop(); // pop data packet 1
+
+ auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front());
+ bool eq_len = (data2->length() == recovered->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data2->data(), recovered->data(), recovered->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+TEST(FECtestRS, RSTestLargerBlocks) {
+ // use RS k = 4 N = 7
+ std::string fec_str = "RS_K4_N7";
+ Encoder encoder(fec_str);
+ Decoder decoder(fec_str);
+
+ PacketFactory pf;
+
+ core::Name name("b001::");
+
+ auto data1 = pf.createData(name, 1, 50, 1);
+ const uint8_t *data1_ptr = data1->data();
+
+ auto data2 = pf.createData(name, 2, 45, 2);
+ const uint8_t *data2_ptr = data2->data();
+
+ auto data3 = pf.createData(name, 3, 12, 3);
+ const uint8_t *data3_ptr = data3->data();
+
+ auto data4 = pf.createData(name, 4, 20, 4);
+ const uint8_t *data4_ptr = data4->data();
+
+ // encoding
+ uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType());
+ encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+ encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE,
+ metadata);
+
+ // create fec packet
+ auto data5 = pf.createData(name, 5, encoder.fec_packets_.front());
+ encoder.fec_packets_.pop(); // pop 5
+ encoder.fec_packets_.pop(); // pop 6
+ auto data7 = pf.createData(name, 7, encoder.fec_packets_.front());
+
+ // decoding packets: lost data 3 and data 4
+ decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE);
+ decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ // test payload pointers off the original packets
+ EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr);
+ EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr);
+ EXPECT_EQ((const uint8_t *)data3->data(), data3_ptr);
+ EXPECT_EQ((const uint8_t *)data4->data(), data4_ptr);
+
+ // get recovered packet
+ EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4);
+ decoder.recovered_packets_.pop(); // pop data 1
+ decoder.recovered_packets_.pop(); // pop data 2
+ auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front());
+ decoder.recovered_packets_.pop(); // pop data 3
+ auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front());
+
+ bool eq_len = (data3->length() == recovered3->length());
+ EXPECT_TRUE(eq_len);
+ int ret = -1;
+ if (eq_len)
+ ret = memcmp(data3->data(), recovered3->data(), recovered3->length());
+ EXPECT_EQ(ret, (int)0);
+
+ eq_len = (data4->length() == recovered4->length());
+ EXPECT_TRUE(eq_len);
+ ret = -1;
+ if (eq_len)
+ ret = memcmp(data4->data(), recovered4->data(), recovered4->length());
+ EXPECT_EQ(ret, (int)0);
+}
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_fec_reedsolomon.cc b/libtransport/src/test/test_fec_reedsolomon.cc
new file mode 100644
index 000000000..0973069b1
--- /dev/null
+++ b/libtransport/src/test/test_fec_reedsolomon.cc
@@ -0,0 +1,269 @@
+
+/*
+ * 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 <glog/logging.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <protocols/fec/rs.h>
+
+#include <algorithm>
+#include <iostream>
+#include <random>
+
+namespace transport {
+namespace protocol {
+
+double ReedSolomonTest(int k, int n, int seq_offset, int size) {
+ fec::RSEncoder encoder(k, n, seq_offset);
+ fec::RSDecoder decoder(k, n, seq_offset);
+
+ using BufferMetadata = std::pair<fec::buffer, uint32_t>;
+
+ std::vector<BufferMetadata> tx_block(k);
+ std::vector<BufferMetadata> rx_block(k);
+ int count = 0;
+ int run = 0;
+
+ // Setup random engine
+ std::random_device
+ rd; // Will be used to obtain a seed for the random number engine
+ std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
+ std::uniform_int_distribution<> dis(0, 99);
+
+ int iterations = 100;
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ encoder.setFECCallback([&tx_block](fec::BufferArray &repair_packets) {
+ for (auto &p : repair_packets) {
+ // Append repair symbols to tx_block
+ tx_block.emplace_back(p.getBuffer(), p.getMetadata());
+ }
+ });
+
+ decoder.setFECCallback(
+ [&tx_block, &count, &k](fec::BufferArray &source_packets) {
+ for (int i = 0; i < k; i++) {
+ // Compare decoded source packets with original transmitted packets.
+ if (*tx_block[i].first != *source_packets[i].getBuffer() ||
+ tx_block[i].second != source_packets[i].getMetadata()) {
+ count++;
+ }
+ }
+ });
+
+ do {
+ // Discard eventual packet appended in previous callback call
+ tx_block.erase(tx_block.begin() + k, tx_block.end());
+ uint32_t _seq_offset = seq_offset;
+
+ // Initialization. Feed encoder with first k source packets
+ for (int i = 0; i < k; i++) {
+ // Get new buffer from pool
+ auto packet = packet_manager.getMemBuf();
+
+ // Let's append a bit less than size, so that the FEC class will take care
+ // of filling the rest with zeros
+ auto cur_size = size - dis(gen);
+
+ // Set payload, saving 2 bytes at the beginning of the buffer for encoding
+ // the length
+ packet->append(cur_size);
+ std::fill(packet->writableData(), packet->writableTail(), i + 1);
+
+ // Set first byte of payload to seq_offset, to reorder at receiver side
+ uint32_t *pkt_head = (uint32_t *)packet->writableData();
+ *pkt_head = _seq_offset++;
+
+ // Set a metadata integer
+ uint32_t metadata = dis(gen);
+
+ // Store packet in tx buffer and clear rx buffer
+ tx_block[i] = std::make_pair(std::move(packet), metadata);
+ }
+
+ // Create the repair packets
+ for (auto &tx : tx_block) {
+ encoder.consume(tx.first, tx.first->writableBuffer()[0], 0, tx.second);
+ }
+
+ // Simulate transmission on lossy channel
+ std::vector<bool> losses(n, false);
+ for (int i = 0; i < n - k; i++) losses[i] = true;
+
+ int rxi = 0;
+ std::shuffle(losses.begin(), losses.end(), gen);
+ for (int i = 0; i < n && rxi < k; i++)
+ if (losses[i] == false) {
+ rx_block[rxi++] = tx_block[i];
+ if (i < k) {
+ // Source packet
+ uint32_t index = *((uint32_t *)rx_block[rxi - 1].first->data());
+ decoder.consumeSource(rx_block[rxi - 1].first, index, 0,
+ rx_block[rxi - 1].second);
+ } else {
+ // Repair packet
+ decoder.consumeRepair(rx_block[rxi - 1].first);
+ }
+ }
+
+ decoder.clear();
+ encoder.clear();
+ } while (++run < iterations);
+
+ return count;
+}
+
+void ReedSolomonMultiBlockTest(int n_sourceblocks) {
+ int k = 16;
+ int n = 24;
+ int size = 1000;
+
+ fec::RSEncoder encoder(k, n);
+ fec::RSDecoder decoder(k, n);
+
+ // Setup random engine
+ std::random_device
+ rd; // Will be used to obtain a seed for the random number engine
+ std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
+ std::uniform_int_distribution<> dis(0, 99);
+
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ std::vector<std::pair<fec::buffer, uint32_t>> tx_block;
+ std::vector<std::pair<fec::buffer, uint32_t>> rx_block;
+ int count = 0;
+ int i = 0;
+
+ // Receiver will receive packet for n_sourceblocks in a random order.
+ int total_packets = n * n_sourceblocks;
+ int tx_packets = k * n_sourceblocks;
+
+ encoder.setFECCallback([&tx_block, &rx_block, &i, &n, &k,
+ &encoder](fec::BufferArray &repair_packets) {
+ for (auto &p : repair_packets) {
+ // Append repair symbols to tx_block
+ ++i;
+ tx_block.emplace_back(std::move(p.getBuffer()), i);
+ }
+
+ EXPECT_EQ(tx_block.size(), size_t(n));
+
+ // Select k packets to send, including at least one symbol. We start
+ // from the end for this reason.
+ for (int j = n - 1; j > n - k - 1; j--) {
+ rx_block.emplace_back(std::move(tx_block[j]));
+ }
+
+ // Clear tx block for next source block
+ tx_block.clear();
+ encoder.clear();
+ });
+
+ // The decode callback must be called exactly n_sourceblocks times
+ decoder.setFECCallback([&count](fec::BufferArray &source_packets) {
+ // Check buffers
+ for (auto &packet : source_packets) {
+ auto packet_index = ((uint32_t *)packet.getBuffer()->writableData())[0];
+ EXPECT_EQ(packet_index, packet.getIndex())
+ << "Packet index: " << packet_index
+ << " -- FEC Index: " << packet.getIndex();
+ }
+ count++;
+ });
+
+ // Produce n * n_sourceblocks
+ // - ( k ) * n_sourceblocks source packets
+ // - (n - k) * n_sourceblocks symbols)
+ for (i = 0; i < total_packets; i++) {
+ // Get new buffer from pool
+ auto packet = packet_manager.getMemBuf();
+
+ // Let's append a bit less than size, so that the FEC class will take care
+ // of filling the rest with zeros
+ auto cur_size = size - dis(gen);
+
+ // Set payload, saving 2 bytes at the beginning of the buffer for encoding
+ // the length
+ packet->append(cur_size);
+ packet->trimStart(2);
+ std::fill(packet->writableData(), packet->writableTail(), i + 1);
+
+ // Set first byte of payload to i, to reorder at receiver side
+ ((uint32_t *)packet->writableData())[0] = uint32_t(i);
+
+ // Store packet in tx buffer
+ tx_block.emplace_back(packet, i);
+
+ // Feed encoder with packet
+ encoder.consume(packet, i);
+ }
+
+ // Here rx_block must contains k * n_sourceblocks packets
+ EXPECT_EQ(size_t(tx_packets), size_t(rx_block.size()));
+
+ // Lets shuffle the rx_block before starting feeding the decoder.
+ std::shuffle(rx_block.begin(), rx_block.end(), gen);
+
+ for (auto &p : rx_block) {
+ int index = p.second % n;
+ if (index < k) {
+ // Source packet
+ decoder.consumeSource(p.first, p.second);
+ } else {
+ // Repair packet
+ decoder.consumeRepair(p.first);
+ }
+ }
+
+ // Simple test to check we get all the source packets
+ EXPECT_EQ(count, n_sourceblocks);
+}
+
+/**
+ * @brief Use foreach_rs_fec_type to automatically generate the code of the
+ * tests and avoid copy/paste the same function.
+ */
+#define _(name, k, n) \
+ TEST(ReedSolomonTest, RSK##k##N##n) { \
+ int K = k; \
+ int N = n; \
+ int seq_offset = 0; \
+ int size = 1000; \
+ EXPECT_LE(ReedSolomonTest(K, N, seq_offset, size), 0); \
+ seq_offset = 12345; \
+ EXPECT_LE(ReedSolomonTest(K, N, seq_offset, size), 0); \
+ }
+foreach_rs_fec_type
+#undef _
+
+TEST(ReedSolomonMultiBlockTest, RSMB10) {
+ int blocks = 1;
+ ReedSolomonMultiBlockTest(blocks);
+}
+
+TEST(ReedSolomonMultiBlockTest, RSMB100) {
+ int blocks = 100;
+ ReedSolomonMultiBlockTest(blocks);
+}
+
+TEST(ReedSolomonMultiBlockTest, RSMB1000) {
+ int blocks = 1000;
+ ReedSolomonMultiBlockTest(blocks);
+}
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_fec_rely_wrapper.cc b/libtransport/src/test/test_fec_rely_wrapper.cc
new file mode 100644
index 000000000..764e4dd2d
--- /dev/null
+++ b/libtransport/src/test/test_fec_rely_wrapper.cc
@@ -0,0 +1,247 @@
+/*
+ * 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 <glog/logging.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <hicn/transport/interfaces/socket_options_default_values.h>
+#include <hicn/transport/utils/log.h>
+#include <protocols/fec/rely.h>
+
+#include <queue>
+#include <random>
+
+namespace transport {
+namespace protocol {
+
+using SavedPacketMap =
+ std::map<uint32_t, std::pair<core::ContentObject::Ptr, uint32_t>>;
+
+std::string printMissing(const SavedPacketMap &missing) {
+ std::stringstream stream;
+
+ for (auto &[seq, packet] : missing) {
+ stream << " " << seq;
+ }
+
+ stream << "\n";
+
+ return stream.str();
+}
+
+/**
+ * @brief Test encode-decode operations performed using the wrapper for rely
+ *
+ * @param k Number of source symbols
+ * @param n Sum of source symbols and repair symbols
+ * @param max_packet_size The max packet size the decoder will expect.
+ * @param timeout The timeout used by rely
+ * https://rely.steinwurf.com/docs/6.1.0/design/timeout_configuration.html
+ * @param max_iterations The number of packets to send
+ * @param loss_rate The loss rate
+ */
+void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size,
+ int64_t /* timeout */, uint32_t max_iterations,
+ int loss_rate) {
+ // Create 1 encoder and 1 decoder
+ fec::RelyEncoder _encoder(k, n);
+ fec::RelyDecoder _decoder(k, n);
+
+ // Seed the pseudo-random with known value to always get same loss pattern
+ std::mt19937 gen(k *
+ n); // Standard mersenne_twister_engine seeded with rd();
+
+ // We will interact with rely encoder/decoder using the interface
+ fec::ProducerFEC &encoder = _encoder;
+ fec::ConsumerFEC &decoder = _decoder;
+
+ // Initialize current iteration
+ uint32_t iterations = 0;
+
+ // Packet allocator
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ // Store packets to verify them in the decoder callback
+ SavedPacketMap saved_packets;
+
+ // Save repair packets here in encoder callback
+ std::queue<fec::buffer> pending_repair_packets;
+
+ // Set callback called by encoder when a buffer is required.
+ encoder.setBufferCallback([](std::size_t size) {
+ auto ret =
+ core::PacketManager<>::getInstance().getPacket<core::ContentObject>(
+ transport::interface::default_values::packet_format);
+ ret->updateLength(size);
+ ret->append(size);
+ ret->trimStart(ret->headerSize());
+ DCHECK(ret->length() >= size);
+
+ return ret;
+ });
+
+ // Set callback to be called by encoder when repair packets are ready
+ encoder.setFECCallback([&iterations, &pending_repair_packets, &n,
+ &k](fec::BufferArray &packets) {
+ // We must get n - k symbols
+ EXPECT_EQ(packets.size(), n - k);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Got " << packets.size() << " symbols";
+
+ // Save symbols in pending_repair_packets queue and increment iterations
+ for (auto &packet : packets) {
+ ++iterations;
+ pending_repair_packets.push(packet.getBuffer());
+ }
+ });
+
+ // Set callback to be called when decoder recover a packet
+ decoder.setFECCallback([&saved_packets](fec::BufferArray &packets) {
+ for (auto &packet : packets) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Recovering packet " << packet.getIndex();
+
+ // Ensure recovered packet is in packets actually produced by encoder
+ auto original = saved_packets.find(packet.getIndex());
+ ASSERT_TRUE(original != saved_packets.end());
+ auto &original_packet = *original->second.first;
+
+ // Remove additional headers at the beginning of the packet. This may
+ // change in the future.
+ original_packet.trimStart(60 /* Ip + TCP */ + 32 /* Rely header */ +
+ 4 /* Packet size */);
+
+ // Recovered packet should be equal to the original one
+ EXPECT_TRUE(original_packet == *packet.getBuffer());
+
+ // Also metadata should correspond
+ EXPECT_TRUE(original->second.second == packet.getMetadata());
+
+ // Restore removed headers
+ original_packet.prepend(60 + 32 + 4);
+
+ // Erase packet from saved packet list
+ saved_packets.erase(original);
+ }
+ });
+
+ // Send max_iterations packets from encoder to decoder
+ std::uniform_int_distribution<> dis(0, 1299);
+ while (iterations < max_iterations) {
+ // Create a payload, the size is between 50 and 1350 bytes.
+ auto payload_size = 50 + (dis(gen));
+ uint8_t payload[max_packet_size];
+ std::generate(payload, payload + payload_size, gen);
+
+ // Get a packet from global pool and set name
+ auto buffer = packet_manager.getPacket<core::ContentObject>(
+ transport::interface::default_values::packet_format);
+ buffer->setName(core::Name("b001::abcd", iterations));
+
+ // Get offset
+ auto offset = buffer->headerSize();
+
+ // Copy payload into packet. We keep the payload to compare returned packet
+ // with original one (since rely encoder does modify the packet by adding
+ // its own header).
+ buffer->appendPayload(payload, payload_size);
+
+ // Set an u32 metadata to pass altogether with the buffer
+ uint32_t metadata = dis(gen);
+
+ // Save packet in the saving_packets list
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Saving packet with index " << iterations;
+ saved_packets.emplace(iterations, std::make_pair(buffer, metadata));
+
+ // Feed buffer into the encoder. This will eventually trigger a call to the
+ // FEC callback as soon as k packets are fed into the endocer.
+ encoder.onPacketProduced(*buffer, offset, metadata);
+
+ // Check returned packet. We calculate the difference in size and we compare
+ // only the part of the returned packet corresponding to the original
+ // payload. Rely should only add a header and should not modify the actual
+ // payload content. If it does it, this check will fail.
+ auto diff = buffer->length() - payload_size - offset;
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Difference is " << diff;
+ auto cmp =
+ std::memcmp(buffer->data() + offset + diff, payload, payload_size);
+ EXPECT_FALSE(cmp);
+
+ // Drop condition. Id addition to the loss rate, we ensure that no drops are
+ // perfomed in the last 10% of the total iterations. This is done because
+ // rely uses a sliding-window mechanism to recover, and if we suddenly stop
+ // we may not be able to recover missing packets that would be recovered
+ // using future packets that are not created in the test. For this reason,
+ // we ensure the test ends without losses.
+#define DROP_CONDITION(loss_rate, max_iterations) \
+ (dis(gen)) >= loss_rate || iterations >= max_iterations * 0.9
+
+ // Handle the source packet to the decoder, id drop condition returns true
+ if (DROP_CONDITION(loss_rate, max_iterations)) {
+ // Pass packet to decoder
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Passing packet " << buffer->getName().getSuffix()
+ << " to decoder";
+ decoder.onDataPacket(*buffer, offset, metadata);
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Packet " << buffer->getName().getSuffix() << " dropped";
+ }
+
+ // Check if previous call to encoder.consume() generated repair packets,
+ // and if yes, feed them to the decoder.
+ while (pending_repair_packets.size()) {
+ // Also repair packets can be lost
+ if (DROP_CONDITION(loss_rate, max_iterations)) {
+ auto &packet = pending_repair_packets.front();
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Passing packet " << iterations << " to decoder";
+ core::ContentObject &co = (core::ContentObject &)(*packet);
+ decoder.onDataPacket(co, 0);
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Packet (repair) " << iterations << " dropped";
+ }
+
+ // Remove packet from the queue
+ pending_repair_packets.pop();
+ }
+
+ ++iterations;
+ }
+
+ // We expect this test to terminate with a full recover of all the packets and
+ // 0.001 residual losses
+ EXPECT_LE(saved_packets.size(), iterations * 0.001)
+ << printMissing(saved_packets);
+}
+
+/**
+ * @brief Use foreach_rely_fec_type to automatically generate the code of the
+ * tests and avoid copy/paste the same function.
+ */
+#define _(name, k, n) \
+ TEST(RelyTest, RelyK##k##N##n) { \
+ int K = k; \
+ int N = n; \
+ uint32_t max_iterations = 1000; \
+ int size = 1400; \
+ int64_t timeout = 120; \
+ int loss_rate = 10; \
+ testRelyEncoderDecoder(K, N, size, timeout, max_iterations, loss_rate); \
+ }
+foreach_rely_fec_type
+#undef _
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_fixed_block_allocator.cc b/libtransport/src/test/test_fixed_block_allocator.cc
new file mode 100644
index 000000000..33e048031
--- /dev/null
+++ b/libtransport/src/test/test_fixed_block_allocator.cc
@@ -0,0 +1,211 @@
+/*
+ * 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 <hicn/transport/core/global_object_pool.h>
+#include <hicn/transport/utils/event_thread.h>
+#include <hicn/transport/utils/fixed_block_allocator.h>
+
+namespace utils {
+
+class FixedBlockAllocatorTest : public ::testing::Test {
+ protected:
+ static inline const std::size_t default_size = 2048;
+ static inline const std::size_t default_n_buffer = 1024;
+
+ // Get fixed block allocator_ of 1024 buffers of size 2048 bytes
+ FixedBlockAllocatorTest()
+ : allocator_(
+ ::utils::FixedBlockAllocator<default_size,
+ default_n_buffer>::getInstance()) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~FixedBlockAllocatorTest() {
+ // 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).
+ allocator_.reset();
+ }
+
+ virtual void TearDown() {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ allocator_.reset();
+ }
+
+ static bool pointerIsAligned(const void *pointer, size_t byte_count) {
+ // Sanity check
+ EXPECT_THAT(reinterpret_cast<std::uintptr_t>(pointer) &
+ (alignof(std::max_align_t) - 1),
+ testing::Eq(std::uintptr_t(0)));
+
+ return uintptr_t(pointer) % byte_count == 0;
+ }
+
+ ::utils::FixedBlockAllocator<default_size, default_n_buffer> &allocator_;
+};
+
+TEST_F(FixedBlockAllocatorTest, DefaultChecks) {
+ EXPECT_EQ(allocator_.blockSize(), default_size);
+ EXPECT_EQ(allocator_.blockCount(), default_n_buffer);
+ EXPECT_EQ(allocator_.allocations(), 0UL);
+ EXPECT_EQ(allocator_.deallocations(), 0UL);
+ EXPECT_EQ(allocator_.blocksInUse(), 0UL);
+
+ // Allocate one single block of memory
+ auto block = allocator_.allocateBlock();
+
+ ASSERT_THAT(block, testing::NotNull());
+
+ // Check statistics
+ EXPECT_EQ(allocator_.allocations(), 1UL);
+ EXPECT_EQ(allocator_.deallocations(), 0UL);
+ EXPECT_EQ(allocator_.blocksInUse(), 1UL);
+
+ // Deallocate it
+ allocator_.deallocateBlock(block);
+
+ // check statistics
+ EXPECT_EQ(allocator_.allocations(), 1UL);
+ EXPECT_EQ(allocator_.deallocations(), 1UL);
+ EXPECT_EQ(allocator_.blocksInUse(), 0UL);
+
+ // Test reset
+ allocator_.reset();
+
+ EXPECT_EQ(allocator_.blockSize(), default_size);
+ EXPECT_EQ(allocator_.blockCount(), default_n_buffer);
+ EXPECT_EQ(allocator_.allocations(), 0UL);
+ EXPECT_EQ(allocator_.deallocations(), 0UL);
+ EXPECT_EQ(allocator_.blocksInUse(), 0UL);
+}
+
+TEST_F(FixedBlockAllocatorTest, CheckMemoryIsReused) {
+ // Get one block. As it is the first one, it will be retrieved from the pool
+ auto block = allocator_.allocateBlock();
+
+ // Make sure block is valid
+ ASSERT_THAT(block, testing::NotNull());
+
+ // Release block
+ allocator_.deallocateBlock(block);
+
+ // Get same memory block again
+ auto block2 = allocator_.allocateBlock();
+
+ // Make sure memory is reused
+ ASSERT_EQ(block, block2);
+
+ // Get a third block
+ auto block3 = allocator_.allocateBlock();
+
+ // Make sure is different memory
+ ASSERT_NE(block2, block3);
+
+ // Deallocate both and check we get back the laso one
+ allocator_.deallocateBlock(block2);
+ allocator_.deallocateBlock(block3);
+
+ auto block4 = allocator_.allocateBlock();
+ ASSERT_EQ(block3, block4);
+}
+
+TEST_F(FixedBlockAllocatorTest, CheckMemoryIsContiguous) {
+ // Get one block. As it is the first one, it will be retrieved from the pool
+ auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+
+ // Make sure block is valid
+ ASSERT_THAT(block, testing::NotNull());
+
+ // Get another block
+ auto block2 = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+
+ // Make sure block is valid
+ ASSERT_THAT(block2, testing::NotNull());
+
+ // Check the 2 blocks come from contiguous memory
+ ASSERT_THAT(std::size_t(block2 - block), testing::Eq(default_size));
+}
+
+TEST_F(FixedBlockAllocatorTest, CheckPoolExpansion) {
+ // Get all the blocks we setup when constructing the allocator
+ std::array<uint8_t *, default_n_buffer> blocks;
+ blocks[0] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+ for (std::size_t i = 1; i < default_n_buffer; i++) {
+ blocks[i] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+ ASSERT_THAT(std::size_t(blocks[i] - blocks[i - 1]),
+ testing::Eq(default_size));
+ }
+
+ ASSERT_THAT(allocator_.blockCount(), testing::Eq(default_n_buffer));
+
+ // We should have finished all the blocks belonging to first pool. Let's get
+ // one additional block
+ auto new_block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+
+ // Make sure the block count doubled its size
+ ASSERT_THAT(allocator_.blockCount(), testing::Eq(2 * default_n_buffer));
+
+ // Check the new block is not contiguous with respect last block in blocks
+ ASSERT_THAT(std::size_t(new_block - blocks[default_n_buffer - 1]),
+ testing::Ne(default_size));
+}
+
+TEST_F(FixedBlockAllocatorTest, CheckMemoryIsAligned) {
+ for (std::size_t i = 0; i < default_n_buffer; i++) {
+ auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock());
+ ASSERT_THAT(pointerIsAligned(block, alignof(std::max_align_t)),
+ testing::IsTrue);
+ }
+}
+
+TEST_F(FixedBlockAllocatorTest, Multithreading) {
+ // Create 4 threads
+ utils::EventThread threads[4];
+ ::utils::FixedBlockAllocator<default_size, default_n_buffer>
+ *allocator_addresses[4] = {nullptr, nullptr, nullptr, nullptr};
+ int i = 0;
+ for (auto &t : threads) {
+ t.add([&allocator_addresses, i]() {
+ auto &allocator =
+ ::utils::FixedBlockAllocator<default_size,
+ default_n_buffer>::getInstance();
+ allocator_addresses[i] = &allocator;
+ });
+ i++;
+ }
+
+ // Stop threads
+ for (auto &t : threads) {
+ t.stop();
+ }
+
+ // Check the instance of allocator was different for each thread
+ for (int i = 0; i < 4; i++) {
+ for (int j = i + 1; j < 4; j++) {
+ ASSERT_NE(allocator_addresses[i], allocator_addresses[j]);
+ }
+ }
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/test/test_indexer.cc b/libtransport/src/test/test_indexer.cc
new file mode 100644
index 000000000..9c12e2037
--- /dev/null
+++ b/libtransport/src/test/test_indexer.cc
@@ -0,0 +1,322 @@
+
+/*
+ * 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 <protocols/incremental_indexer_bytestream.h>
+#include <protocols/indexer.h>
+#include <protocols/rtc/rtc_indexer.h>
+
+#include <algorithm>
+#include <iostream>
+#include <random>
+
+namespace transport {
+namespace protocol {
+
+class IncrementalIndexerTest : public ::testing::Test {
+ protected:
+ IncrementalIndexerTest() : indexer_(nullptr, nullptr) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~IncrementalIndexerTest() {
+ // 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).
+ }
+
+ IncrementalIndexer indexer_;
+};
+
+class RtcIndexerTest : public ::testing::Test {
+ protected:
+ RtcIndexerTest() : indexer_(nullptr, nullptr) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~RtcIndexerTest() {
+ // 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).
+ indexer_.setFirstSuffix(0);
+ indexer_.reset();
+ }
+
+ virtual void TearDown() {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+
+ static const constexpr uint32_t LIMIT = (1 << 31);
+ rtc::RtcIndexer<LIMIT> indexer_;
+};
+
+void testIncrement(Indexer &indexer) {
+ // As a first index we should get zero
+ auto index = indexer.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(0));
+
+ // Check if the sequence works for consecutive incremental numbers
+ for (uint32_t i = 1; i < 4096; i++) {
+ index = indexer.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+
+ index = indexer.getNextSuffix();
+ EXPECT_NE(index, uint32_t(0));
+}
+
+void testJump(Indexer &indexer) {
+ // Fist suffix is 0
+ auto index = indexer.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(0));
+
+ // Next suffix should be 1, but we jump to 12345
+ uint32_t jump = 12345;
+ indexer.jumpToIndex(jump);
+
+ // This takes place immediately
+ index = indexer.getNextSuffix();
+ EXPECT_EQ(index, jump);
+}
+
+TEST_F(IncrementalIndexerTest, TestReset) {
+ testIncrement(indexer_);
+
+ // Reset the indexer
+ indexer_.reset();
+
+ // Now it should startfrom zero again
+ for (uint32_t i = 0; i < 4096; i++) {
+ auto index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+}
+
+TEST_F(IncrementalIndexerTest, TestGetSuffix) { testIncrement(indexer_); }
+
+TEST_F(IncrementalIndexerTest, TestGetNextReassemblySegment) {
+ // Test suffixes for reassembly are not influenced by download suffixed
+ // increment
+ for (uint32_t i = 0; i < 4096; i++) {
+ auto index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+
+ for (uint32_t i = 0; i < 4096; i++) {
+ auto index = indexer_.getNextReassemblySegment();
+ EXPECT_EQ(index, i);
+ }
+}
+
+TEST_F(IncrementalIndexerTest, TestJumpToIndex) { testJump(indexer_); }
+
+TEST_F(IncrementalIndexerTest, TestGetFinalSuffix) {
+ // Since final suffix hasn't been discovered, it should be invalid_index
+ auto final_suffix = indexer_.getFinalSuffix();
+ ASSERT_EQ(final_suffix, Indexer::invalid_index);
+}
+
+TEST_F(IncrementalIndexerTest, TestMaxLimit) {
+ // Jump to max value for uint32_t
+ indexer_.jumpToIndex(std::numeric_limits<uint32_t>::max());
+ auto ret = indexer_.getNextSuffix();
+ ASSERT_EQ(ret, Indexer::invalid_index);
+
+ // Now the indexer should always return invalid_index
+ for (uint32_t i = 0; i < 4096; i++) {
+ ret = indexer_.getNextSuffix();
+ EXPECT_EQ(ret, Indexer::invalid_index);
+ }
+}
+
+TEST_F(IncrementalIndexerTest, TestSetFirstSuffix) {
+ // Set first suffix before starting
+ uint32_t start = 1234567890;
+ indexer_.setFirstSuffix(1234567890);
+
+ // The first suffix set should take place only after a reset
+ auto index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(0));
+
+ indexer_.reset();
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, start);
+}
+
+TEST_F(IncrementalIndexerTest, TestIsFinalSuffixDiscovered) {
+ // Final suffix should not be discovererd
+ auto ret = indexer_.isFinalSuffixDiscovered();
+ EXPECT_FALSE(ret);
+}
+
+TEST_F(RtcIndexerTest, TestReset) {
+ // Without setting anything this indexer should behave exactly as the
+ // incremental indexer for the getNextSuffix()
+ testIncrement(indexer_);
+
+ // Reset the indexer
+ indexer_.reset();
+
+ // Now it should startfrom zero again
+ for (uint32_t i = 0; i < 4096; i++) {
+ auto index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+}
+
+TEST_F(RtcIndexerTest, TestGetNextSuffix) {
+ // Without setting anything this indexer should behave exactly as the
+ // incremental indexer for the getNextSuffix()
+ testIncrement(indexer_);
+}
+
+TEST_F(RtcIndexerTest, TestGetNextReassemblySegment) {
+ // This indexer should not provide reassembly segments since they are not
+ // required for rtc
+ try {
+ indexer_.getNextReassemblySegment();
+ // We should not reach this point
+ FAIL() << "Exception expected here";
+ } catch (const errors::RuntimeException &exc) {
+ // OK correct exception
+ } catch (...) {
+ FAIL() << "Wrong exception thrown";
+ }
+}
+
+TEST_F(RtcIndexerTest, TestGetFinalSuffix) {
+ // Final suffix should be eqaul to LIMIT
+ ASSERT_EQ(indexer_.getFinalSuffix(), uint32_t(LIMIT));
+}
+
+TEST_F(RtcIndexerTest, TestJumpToIndex) { testJump(indexer_); }
+
+TEST_F(RtcIndexerTest, TestIsFinalSuffixDiscovered) {
+ // This method should always return true
+ EXPECT_TRUE(indexer_.isFinalSuffixDiscovered());
+}
+
+TEST_F(RtcIndexerTest, TestMaxLimit) {
+ // Once reached the LIMIT, this indexer should restart from 0
+
+ // Jump to max value for uint32_t
+ indexer_.jumpToIndex(LIMIT);
+ testIncrement(indexer_);
+}
+
+TEST_F(RtcIndexerTest, TestEnableFec) {
+ // Here we enable the FEC and we check we receive indexes for souece packets
+ // only
+ indexer_.enableFec(fec::FECType::RS_K1_N3);
+
+ // We did not set NFec, which should be zero. So we get only indexes for
+ // Source packets.
+
+ // With this FEC type we should get one source packet every 3 (0 . . 3 . . 6)
+ auto index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(0));
+
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(3));
+
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(6));
+
+ // Change FEC Type
+ indexer_.enableFec(fec::FECType::RS_K10_N30);
+
+ // With this FEC type we should get source packets from 7 to 9
+ for (uint32_t i = 7; i < 10; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+
+ // And then jump to 30
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, uint32_t(30));
+
+ // Let's now jump to a high value
+ indexer_.jumpToIndex(12365);
+ for (uint32_t i = 12365; i < 12369; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(index, i);
+ }
+}
+
+TEST_F(RtcIndexerTest, TestSetNFec) {
+ // Here we enable the FEC and we set a max of 20 fec packets
+ indexer_.enableFec(fec::FECType::RS_K10_N90);
+ indexer_.setNFec(20);
+
+ // We should get indexes up to 29
+ uint32_t index;
+ for (uint32_t i = 0; i < 30; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(i, index);
+ }
+
+ // Then it should jump to 90
+ for (uint32_t i = 90; i < 99; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_EQ(i, index);
+ }
+
+ // Let's set NFEC > 80
+ indexer_.setNFec(150);
+}
+
+TEST_F(RtcIndexerTest, TestSetNFecWithOffset) {
+ // Here we enable the FEC and we set a max of 20 fec packets
+ const constexpr uint32_t first_suffix = 7;
+ indexer_.setFirstSuffix(first_suffix);
+ indexer_.reset();
+ indexer_.enableFec(fec::FECType::RS_K16_N24);
+ indexer_.setNFec(8);
+
+ uint32_t index;
+ for (uint32_t i = first_suffix; i < 16 + first_suffix; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_FALSE(indexer_.isFec(index));
+ EXPECT_EQ(i, index);
+ }
+
+ for (uint32_t i = first_suffix + 16; i < 16 + 8 + first_suffix; i++) {
+ index = indexer_.getNextSuffix();
+ EXPECT_TRUE(indexer_.isFec(index));
+ EXPECT_EQ(i, index);
+ }
+}
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_interest.cc b/libtransport/src/test/test_interest.cc
new file mode 100644
index 000000000..22dc01455
--- /dev/null
+++ b/libtransport/src/test/test_interest.cc
@@ -0,0 +1,308 @@
+/*
+ * 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 <hicn/transport/core/interest.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
+#include <test/packet_samples.h>
+
+#include <climits>
+#include <random>
+#include <vector>
+
+namespace transport {
+
+namespace core {
+
+namespace {
+// The fixture for testing class Foo.
+class InterestTest : public ::testing::Test {
+ protected:
+ InterestTest()
+ : name_("b001::123|321"), interest_(HICN_PACKET_FORMAT_IPV6_TCP) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~InterestTest() {
+ // 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).
+ }
+
+ Name name_;
+
+ Interest interest_;
+
+ std::vector<uint8_t> buffer_ = {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE),
+ // ICMP6 echo request
+ TCP_HEADER(0x00),
+ // Payload
+ PAYLOAD};
+};
+
+void testFormatConstructor(
+ hicn_packet_format_t format = HICN_PACKET_FORMAT_NONE) {
+ try {
+ Interest interest(format, 0);
+ } catch (...) {
+ char buf[MAXSZ_HICN_PACKET_FORMAT];
+ FAIL() << "ERROR: Unexpected exception thrown for " << buf;
+ }
+}
+
+void testFormatConstructorException(
+ Packet::Format format = HICN_PACKET_FORMAT_NONE) {
+ try {
+ Interest interest(format, 0);
+ FAIL() << "We expected an exception here";
+ } catch (errors::MalformedPacketException &exc) {
+ // Ok right exception
+ } catch (...) {
+ FAIL() << "Wrong exception thrown";
+ }
+}
+
+} // namespace
+
+TEST_F(InterestTest, ConstructorWithFormat) {
+ /**
+ * Without arguments it should be format = HICN_PACKET_FORMAT_NONE.
+ * We expect a crash.
+ */
+
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP);
+ testFormatConstructorException(HICN_PACKET_FORMAT_IPV4_ICMP);
+ testFormatConstructorException(HICN_PACKET_FORMAT_IPV6_ICMP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ testFormatConstructorException(HICN_PACKET_FORMAT_IPV4_ICMP_AH);
+ testFormatConstructorException(HICN_PACKET_FORMAT_IPV6_ICMP_AH);
+}
+
+TEST_F(InterestTest, ConstructorWithName) {
+ /**
+ * Without arguments it should be format = HICN_PACKET_FORMAT_NONE.
+ * We expect a crash.
+ */
+ Name n("b001::1|123");
+
+ try {
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP, n);
+ } catch (...) {
+ FAIL() << "ERROR: Unexpected exception thrown";
+ }
+}
+
+TEST_F(InterestTest, ConstructorWithBuffer) {
+ // Ensure buffer is interest
+#if 0
+ auto ret = Interest::isInterest(&buffer_[0]);
+ EXPECT_TRUE(ret);
+#endif
+
+ // Create interest from buffer
+ try {
+ Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size());
+ } catch (...) {
+ FAIL() << "ERROR: Unexpected exception thrown";
+ }
+
+ std::vector<uint8_t> buffer2{// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(ICMP6_PROTO, 60 + 44),
+ // ICMP6 echo request
+ TCP_HEADER(0x00),
+ // Payload
+ PAYLOAD};
+
+ // Ensure this throws an exception
+ try {
+ Interest interest(Interest::COPY_BUFFER, &buffer2[0], buffer2.size());
+ FAIL() << "We expected an exception here";
+ } catch (errors::MalformedPacketException &exc) {
+ // Ok right exception
+ } catch (...) {
+ FAIL() << "Wrong exception thrown";
+ }
+}
+
+TEST_F(InterestTest, SetGetName) {
+ // Create interest from buffer
+ Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size());
+
+ // Get name
+ auto n = interest.getName();
+
+ // ensure name is b002::ca|1
+ Name n2("b002::ca|1");
+ auto ret = (n == n2);
+
+ EXPECT_TRUE(ret);
+
+ Name n3("b003::1234|1234");
+
+ // Change name to b003::1234|1234
+ interest.setName(n3);
+
+ // Check name was set
+ n = interest.getName();
+ ret = (n == n3);
+ EXPECT_TRUE(ret);
+}
+
+TEST_F(InterestTest, SetGetLocator) {
+ // Create interest from buffer
+ Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size());
+
+ // Get locator
+ auto l = interest.getLocator();
+
+ hicn_ip_address_t address;
+ inet_pton(AF_INET6, "b006::ab:cdab:cdef", &address);
+ auto ret = !hicn_ip_address_cmp(&l, &address);
+
+ EXPECT_TRUE(ret);
+
+ // Set different locator
+ inet_pton(AF_INET6, "2001::1234::4321::abcd::", &address);
+
+ // Set it on interest
+ interest.setLocator(address);
+
+ // Check it was set
+ l = interest.getLocator();
+ ret = !hicn_ip_address_cmp(&l, &address);
+
+ EXPECT_TRUE(ret);
+}
+
+TEST_F(InterestTest, SetGetLifetime) {
+ // Create interest from buffer
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP);
+ const constexpr uint32_t lifetime = 10000;
+
+ // Set lifetime
+ interest.setLifetime(lifetime);
+
+ // Get lifetime
+ auto l = interest.getLifetime();
+
+ // Ensure they are the same
+ EXPECT_EQ(l, lifetime);
+}
+
+TEST_F(InterestTest, HasManifest) {
+ // Create interest from buffer
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP);
+
+ // Let's expect anexception here
+ try {
+ interest.setPayloadType(PayloadType::UNSPECIFIED);
+ FAIL() << "We expect an esception here";
+ } catch (errors::RuntimeException &exc) {
+ // Ok right exception
+ } catch (...) {
+ FAIL() << "Wrong exception thrown";
+ }
+
+ interest.setPayloadType(PayloadType::DATA);
+ EXPECT_FALSE(interest.hasManifest());
+
+ interest.setPayloadType(PayloadType::MANIFEST);
+ EXPECT_TRUE(interest.hasManifest());
+}
+
+TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) {
+ // Create interest from buffer
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP);
+
+ // Appenad some suffixes, with some duplicates
+ interest.appendSuffix(1);
+ interest.appendSuffix(2);
+ interest.appendSuffix(5);
+ interest.appendSuffix(3);
+ interest.appendSuffix(4);
+ interest.appendSuffix(5);
+ interest.appendSuffix(5);
+ interest.appendSuffix(5);
+ interest.appendSuffix(5);
+ interest.appendSuffix(5);
+
+ // Encode them in wire format
+ interest.encodeSuffixes();
+
+ // Iterate over them. They should be in order and without repetitions
+
+ auto suffix = interest.firstSuffix();
+ auto n_suffixes = interest.numberOfSuffixes();
+
+ for (uint32_t i = 0; i < n_suffixes; i++) {
+ EXPECT_EQ(*(suffix + i), i);
+ }
+}
+
+TEST_F(InterestTest, AppendSuffixesWithGaps) {
+ // Create interest from buffer
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP);
+
+ // Appenad some suffixes, out of order and with gaps
+ interest.appendSuffix(6);
+ interest.appendSuffix(2);
+ interest.appendSuffix(5);
+ interest.appendSuffix(1);
+
+ // Encode them in wire format
+ interest.encodeSuffixes();
+ EXPECT_TRUE(interest.hasManifest());
+
+ // Check first suffix correctness
+ auto suffix = interest.firstSuffix();
+ EXPECT_NE(suffix, nullptr);
+ EXPECT_EQ(*suffix, 0U);
+
+ // Iterate over them. They should be in order and without repetitions
+ std::vector<uint32_t> expected = {interest.getName().getSuffix(), 1, 2, 5, 6};
+ EXPECT_EQ(interest.numberOfSuffixes(), expected.size());
+
+ for (uint32_t seq : expected) {
+ EXPECT_EQ(*suffix, seq);
+ suffix++;
+ }
+}
+
+TEST_F(InterestTest, InterestWithoutManifest) {
+ // Create interest without manifest
+ Interest interest(HICN_PACKET_FORMAT_IPV6_TCP);
+ auto suffix = interest.firstSuffix();
+
+ EXPECT_FALSE(interest.hasManifest());
+ EXPECT_EQ(interest.numberOfSuffixes(), 0U);
+ EXPECT_EQ(suffix, nullptr);
+}
+
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_memif_connector.cc b/libtransport/src/test/test_memif_connector.cc
new file mode 100644
index 000000000..40f4df927
--- /dev/null
+++ b/libtransport/src/test/test_memif_connector.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 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 <core/memif_connector.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <hicn/transport/utils/chrono_typedefs.h>
+
+namespace transport {
+namespace core {
+
+namespace {
+
+using namespace std::placeholders;
+
+/**
+ * Master memif connector
+ */
+template <int Master>
+class Memif {
+ static inline std::size_t counter = 256;
+ static inline std::size_t total_packets = counter * 4096;
+ static inline std::size_t packet_size = 64;
+
+ public:
+ Memif(asio::io_service &io_service)
+ : io_service_(io_service),
+ memif_connector_(std::make_shared<MemifConnector>(
+ std::bind(&Memif::onPacketReceived, this, _1, _2, _3),
+ std::bind(&Memif::onPacketSent, this, _1, _2),
+ std::bind(&Memif::onClose, this, _1),
+ std::bind(&Memif::onReconnect, this, _1, _2), io_service_,
+ Master ? "test_master" : "test_slave")),
+ recv_counter_(0),
+ sent_counter_(0) {
+ memif_connector_->connect(0 /* Memif ID */, Master /* Is Master */,
+ "@hicntransport/test/memif");
+ }
+
+ void setStart() { t0_ = utils::SteadyTime::now(); }
+
+ void startTest() {
+ if constexpr (!Master) {
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ // Send in busrt of 256 packet per time
+ for (std::size_t i = 0; i < counter; i++) {
+ auto packet = packet_manager.getMemBuf();
+ packet->append(packet_size);
+ memif_connector_->send(packet);
+ sent_counter_++;
+ }
+
+ if (sent_counter_ < total_packets) {
+ asio::post(io_service_, std::bind(&Memif::startTest, this));
+ }
+ } else {
+ setStart();
+ }
+ }
+
+ auto getRecvCounter() { return recv_counter_; }
+ auto getSentCounter() { return sent_counter_; }
+
+ private:
+ void onPacketReceived(Connector *c,
+ const std::vector<utils::MemBuf::Ptr> &buffers,
+ const std::error_code &ec) {
+ if constexpr (Master) {
+ recv_counter_ += buffers.size();
+ if (recv_counter_ == total_packets) {
+ auto t1 = utils::SteadyTime::now();
+ auto delta = utils::SteadyTime::getDurationUs(t0_, t1);
+ double rate = double(recv_counter_) * 1.0e6 / double(delta.count());
+ LOG(INFO) << "rate: " << rate << " packets/s";
+ io_service_.stop();
+ }
+ } else {
+ FAIL() << "Slave should not receive packets";
+ }
+ }
+ void onPacketSent(Connector *c, const std::error_code &ec) {}
+ void onClose(Connector *c) {}
+ void onReconnect(Connector *c, const std::error_code &ec) {}
+
+ private:
+ asio::io_service &io_service_;
+ std::shared_ptr<MemifConnector> memif_connector_;
+ std::size_t recv_counter_;
+ std::size_t sent_counter_;
+ utils::SteadyTime::TimePoint t0_;
+};
+
+using MemifMaster = Memif<1>;
+using MemifSlave = Memif<0>;
+
+} // namespace
+
+class MemifTest : public ::testing::Test {
+ protected:
+ MemifTest() : io_service_(), master_(io_service_), slave_(io_service_) {
+ // You can do set-up work for each test here.
+ }
+
+ void run() {
+ asio::post(io_service_, std::bind(&MemifSlave::startTest, &slave_));
+ master_.startTest();
+ io_service_.run();
+
+ EXPECT_THAT(master_.getRecvCounter(),
+ ::testing::Eq(slave_.getSentCounter()));
+ }
+
+ virtual ~MemifTest() {
+ // 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).
+ }
+
+ protected:
+ asio::io_service io_service_;
+ MemifMaster master_;
+ MemifSlave slave_;
+};
+
+TEST_F(MemifTest, Test) { run(); }
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_packet.cc b/libtransport/src/test/test_packet.cc
new file mode 100644
index 000000000..f2f658932
--- /dev/null
+++ b/libtransport/src/test/test_packet.cc
@@ -0,0 +1,932 @@
+/*
+ * 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 <hicn/transport/core/packet.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <test/packet_samples.h>
+
+#include <climits>
+#include <random>
+#include <vector>
+
+#include "../../lib/src/protocol.h"
+
+namespace transport {
+
+namespace core {
+
+/**
+ * Since packet is an abstract class, we derive a concrete class to be used for
+ * the test.
+ */
+class PacketForTest : public Packet {
+ public:
+ template <typename... Args>
+ PacketForTest(Args &&...args) : Packet(std::forward<Args>(args)...) {}
+
+ virtual ~PacketForTest() {}
+
+ const Name &getName() const override {
+ throw errors::NotImplementedException();
+ }
+
+ Name &getWritableName() override { throw errors::NotImplementedException(); }
+
+ void setName(const Name &name) override {
+ throw errors::NotImplementedException();
+ }
+
+ void setLifetime(uint32_t lifetime) override {
+ throw errors::NotImplementedException();
+ }
+
+ uint32_t getLifetime() const override {
+ throw errors::NotImplementedException();
+ }
+
+ void setLocator(const hicn_ip_address_t &locator) override {
+ throw errors::NotImplementedException();
+ }
+
+ void resetForHash() override { throw errors::NotImplementedException(); }
+
+ hicn_ip_address_t getLocator() const override {
+ throw errors::NotImplementedException();
+ }
+};
+
+namespace {
+// The fixture for testing class Foo.
+class PacketTest : public ::testing::Test {
+ protected:
+ PacketTest()
+ : name_("b001::123|321"),
+ packet(Packet::COPY_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~PacketTest() {
+ // 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).
+ }
+
+ Name name_;
+
+ PacketForTest packet;
+
+ static std::map<uint32_t, std::vector<uint8_t>> raw_packets_;
+
+ std::vector<uint8_t> payload = {
+ 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00,
+ // 0x00, 0x45, 0xa3,
+ // 0xd1, 0xf2, 0x2b,
+ // 0x94, 0x41, 0x22,
+ // 0xc9, 0x00, 0x00,
+ // 0x00, 0x44, 0xa3,
+ // 0xd1, 0xf2, 0x2b,
+ // 0x94, 0x41, 0x22,
+ // 0xc8
+ };
+};
+
+std::map<uint32_t, std::vector<uint8_t>> PacketTest::raw_packets_ = {
+ {HICN_PACKET_FORMAT_IPV6_TCP,
+
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE),
+ // TCP src=0x1234 dst=0x4321, seq=0x0001
+ TCP_HEADER(0x00),
+ // Payload
+ PAYLOAD}},
+
+ {HICN_PACKET_FORMAT_IPV4_TCP,
+ {// IPv4 src=3.13.127.8, dst=192.168.1.92
+ IPV4_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE),
+ // TCP src=0x1234 dst=0x4321, seq=0x0001
+ TCP_HEADER(0x00),
+ // Other
+ PAYLOAD}},
+
+ {HICN_PACKET_FORMAT_IPV4_ICMP,
+ {// IPv4 src=3.13.127.8, dst=192.168.1.92
+ IPV4_HEADER(ICMP_PROTO, 64),
+ // ICMP echo request
+ ICMP_ECHO_REQUEST}},
+
+ {HICN_PACKET_FORMAT_IPV6_ICMP,
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(ICMP6_PROTO, 60),
+ // ICMP6 echo request
+ ICMP6_ECHO_REQUEST}},
+
+ {HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(TCP_PROTO, 20 + 44 + 128),
+ // ICMP6 echo request
+ TCP_HEADER(0x18),
+ // hICN AH header
+ AH_HEADER, SIGNATURE}},
+
+ {HICN_PACKET_FORMAT_IPV4_TCP_AH,
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV4_HEADER(TCP_PROTO, 20 + 44 + 128),
+ // ICMP6 echo request
+ TCP_HEADER(0x18),
+ // hICN AH header
+ AH_HEADER, SIGNATURE}},
+
+ // XXX No flag defined in ICMP header to signal AH header.
+ {HICN_PACKET_FORMAT_IPV4_ICMP_AH,
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV4_HEADER(ICMP_PROTO, 64 + 44),
+ // ICMP6 echo request
+ ICMP_ECHO_REQUEST,
+ // hICN AH header
+ AH_HEADER, SIGNATURE}},
+
+ {HICN_PACKET_FORMAT_IPV6_ICMP_AH,
+ {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca
+ IPV6_HEADER(ICMP6_PROTO, 60 + 44),
+ // ICMP6 echo request
+ ICMP6_ECHO_REQUEST,
+ // hICN AH header
+ AH_HEADER, SIGNATURE}},
+
+};
+
+void testFormatConstructor(Packet::Format format = HICN_PACKET_FORMAT_NONE) {
+ try {
+ PacketForTest packet(HICN_PACKET_TYPE_INTEREST, format);
+ } catch (...) {
+ char buf[MAXSZ_HICN_PACKET_FORMAT];
+ int rc = hicn_packet_format_snprintf(buf, MAXSZ_HICN_PACKET_FORMAT, format);
+ if (rc < 0 || rc >= MAXSZ_HICN_PACKET_FORMAT)
+ snprintf(buf, MAXSZ_HICN_PACKET_FORMAT, "%s", "(error");
+ FAIL() << "ERROR: Unexpected exception thrown for " << buf;
+ }
+}
+
+void testFormatAndAdditionalHeaderConstructor(Packet::Format format,
+ std::size_t additional_header) {
+ PacketForTest packet(HICN_PACKET_TYPE_INTEREST, format, additional_header);
+ // Packet length should be the one of the normal header + the
+ // additional_header
+
+ EXPECT_EQ(packet.headerSize(),
+ Packet::getHeaderSizeFromFormat(format) + additional_header);
+}
+
+void testRawBufferConstructor(std::vector<uint8_t> packet,
+ Packet::Format format) {
+ try {
+ // Try to construct packet from correct buffer
+ PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(),
+ packet.size());
+
+ // Check format is expected one.
+ EXPECT_EQ(p.getFormat(), format);
+
+ // // Try the same using a MemBuf
+ // auto buf = utils::MemBuf::wrapBuffer(&packet[0], packet.size());
+ // buf->append(packet.size());
+ // PacketForTest p2(std::move(buf));
+
+ // EXPECT_EQ(p2.getFormat(), format);
+ } catch (...) {
+ FAIL() << "ERROR: Unexpected exception thrown";
+ }
+
+ try {
+ // Try to construct packet from wrong buffer
+
+ // Modify next header to 0
+ /* ipv6 */
+ packet[6] = 0x00;
+ /* ipv4 */
+ packet[9] = 0x00;
+ PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(),
+ packet.size());
+
+ // Format should fallback to HICN_PACKET_FORMAT_NONE
+ EXPECT_EQ(p.getFormat(), HICN_PACKET_FORMAT_NONE);
+ } catch (errors::MalformedPacketException &exc) {
+ // Ok right exception
+ } catch (...) {
+ FAIL() << "ERROR: Unexpected exception thrown.";
+ }
+}
+
+void getHeaderSizeFromBuffer(std::vector<uint8_t> &packet,
+ std::size_t expected) {
+ auto header_size =
+ PacketForTest::getHeaderSizeFromBuffer(&packet[0], packet.size());
+ EXPECT_EQ(header_size, expected);
+}
+
+void getHeaderSizeFromFormat(Packet::Format format, std::size_t expected) {
+ auto header_size = PacketForTest::getHeaderSizeFromFormat(format);
+ EXPECT_EQ(header_size, expected);
+}
+
+void getPayloadSizeFromBuffer(std::vector<uint8_t> &packet,
+ std::size_t expected) {
+ auto payload_size =
+ PacketForTest::getPayloadSizeFromBuffer(&packet[0], packet.size());
+ EXPECT_EQ(payload_size, expected);
+}
+
+void getFormatFromBuffer(Packet::Format expected,
+ std::vector<uint8_t> &packet) {
+ auto format = PacketForTest::getFormatFromBuffer(&packet[0], packet.size());
+ EXPECT_EQ(format, expected);
+}
+
+void getHeaderSize(std::size_t expected, const PacketForTest &packet) {
+ auto size = packet.headerSize();
+ EXPECT_EQ(size, expected);
+}
+
+void testGetFormat(Packet::Format expected, const Packet &packet) {
+ auto format = packet.getFormat();
+ EXPECT_EQ(format, expected);
+}
+
+} // namespace
+
+TEST_F(PacketTest, ConstructorWithFormat) {
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_ICMP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_ICMP);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV4_ICMP_AH);
+ testFormatConstructor(HICN_PACKET_FORMAT_IPV6_ICMP_AH);
+}
+
+TEST_F(PacketTest, ConstructorWithFormatAndAdditionalHeader) {
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_TCP, 123);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_TCP, 360);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_ICMP, 21);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_ICMP, 444);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH, 555);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH, 321);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_ICMP_AH,
+ 123);
+ testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_ICMP_AH, 44);
+}
+
+TEST_F(PacketTest, ConstructorWithNew) {
+ auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP];
+ auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0],
+ _packet.size(), _packet.size());
+ delete packet_ptr;
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) {
+ auto format = HICN_PACKET_FORMAT_IPV6_TCP;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInetTcp) {
+ auto format = HICN_PACKET_FORMAT_IPV4_TCP;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInetIcmp) {
+ auto format = HICN_PACKET_FORMAT_IPV4_ICMP;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInet6Icmp) {
+ auto format = HICN_PACKET_FORMAT_IPV6_ICMP;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInet6TcpAh) {
+ auto format = HICN_PACKET_FORMAT_IPV6_TCP_AH;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, ConstructorWithRawBufferInetTcpAh) {
+ auto format = HICN_PACKET_FORMAT_IPV4_TCP_AH;
+ testRawBufferConstructor(raw_packets_[format], format);
+}
+
+TEST_F(PacketTest, MoveConstructor) {
+ PacketForTest p0(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP);
+ PacketForTest p1(std::move(p0));
+ EXPECT_EQ(p0.getFormat(), HICN_PACKET_FORMAT_NONE);
+ EXPECT_EQ(p1.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP);
+}
+
+TEST_F(PacketTest, TestGetHeaderSizeFromBuffer) {
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP],
+ IPV6_HDRLEN + TCP_HDRLEN);
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP],
+ IPV4_HDRLEN + TCP_HDRLEN);
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP],
+ IPV6_HDRLEN + 4);
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP],
+ IPV4_HDRLEN + 4);
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH],
+ IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN + 128);
+ getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH],
+ IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN + 128);
+}
+
+TEST_F(PacketTest, TestGetHeaderSizeFromFormat) {
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_TCP,
+ IPV6_HDRLEN + TCP_HDRLEN);
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_TCP,
+ IPV4_HDRLEN + TCP_HDRLEN);
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_ICMP, IPV6_HDRLEN + 4);
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_ICMP, IPV4_HDRLEN + 4);
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN);
+ getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH,
+ IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN);
+}
+
+TEST_F(PacketTest, TestGetPayloadSizeFromBuffer) {
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP], 12);
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP], 12);
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP], 56);
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP], 60);
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH], 0);
+ getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH], 0);
+}
+
+#if 0
+TEST_F(PacketTest, TestIsInterest) {
+ auto ret = PacketForTest::isInterest(&raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0]);
+
+ EXPECT_TRUE(ret);
+}
+#endif
+
+TEST_F(PacketTest, TestGetFormatFromBuffer) {
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_TCP,
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP]);
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_TCP,
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP]);
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_ICMP,
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP]);
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_ICMP,
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP]);
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH]);
+ getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_TCP_AH,
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH]);
+}
+
+// TEST_F(PacketTest, TestReplace) {
+// PacketForTest packet(Packet::WRAP_BUFFER,
+// &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0],
+// raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+
+// // Replace current packet with another one
+// packet.replace(&raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP][0],
+// raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size());
+
+// // Check new format
+// ASSERT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV4_TCP);
+// }
+
+TEST_F(PacketTest, TestPayloadSize) {
+ // Check payload size of existing packet
+ auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP];
+ PacketForTest packet(Packet::WRAP_BUFFER, &_packet[0], _packet.size(),
+ _packet.size());
+
+ EXPECT_EQ(packet.payloadSize(), std::size_t(PAYLOAD_SIZE));
+
+ // Check for dynamic generated packet
+ std::string payload0(1024, 'X');
+
+ // Create the packet
+ PacketForTest packet2(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP);
+
+ // Payload size should now be zero
+ EXPECT_EQ(packet2.payloadSize(), std::size_t(0));
+
+ // Append payload 1 time
+ packet2.appendPayload((const uint8_t *)payload0.c_str(), payload0.size());
+
+ // size should now be 1024
+ EXPECT_EQ(packet2.payloadSize(), std::size_t(1024));
+
+ // Append second payload
+ std::string payload1(1024, 'X');
+ packet2.appendPayload((const uint8_t *)payload1.c_str(), payload1.size());
+
+ // Check size is 2048
+ EXPECT_EQ(packet2.payloadSize(), std::size_t(2048));
+
+ // Append Membuf
+ packet2.appendPayload(utils::MemBuf::copyBuffer(
+ (const uint8_t *)payload1.c_str(), payload1.size()));
+
+ // Check size is 3072
+ EXPECT_EQ(packet2.payloadSize(), std::size_t(3072));
+}
+
+TEST_F(PacketTest, TestHeaderSize) {
+ getHeaderSize(
+ IPV6_HDRLEN + TCP_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP));
+ getHeaderSize(
+ IPV4_HDRLEN + TCP_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP));
+ getHeaderSize(
+ IPV6_HDRLEN + ICMP_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_ICMP));
+ getHeaderSize(
+ IPV4_HDRLEN + ICMP_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_ICMP));
+ getHeaderSize(
+ IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH));
+ getHeaderSize(
+ IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN,
+ PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP_AH));
+}
+
+TEST_F(PacketTest, TestMemBufReference) {
+ // Create packet
+ auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP];
+
+ // Packet was not created as a shared_ptr. If we try to get a membuf shared
+ // ptr we should get an exception.
+ // TODO test with c++ 17
+ // try {
+ // PacketForTest packet(&_packet[0], _packet.size());
+ // auto membuf_ref = packet.acquireMemBufReference();
+ // FAIL() << "The acquireMemBufReference() call should have throwed an "
+ // "exception!";
+ // } catch (const std::bad_weak_ptr &e) {
+ // // Ok
+ // } catch (...) {
+ // FAIL() << "Not expected exception.";
+ // }
+
+ auto packet_ptr = std::make_shared<PacketForTest>(
+ Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size());
+ PacketForTest &packet = *packet_ptr;
+
+ // Acquire a reference to the membuf
+ auto membuf_ref = packet.acquireMemBufReference();
+
+ // Check refcount. It should be 2
+ EXPECT_EQ(membuf_ref.use_count(), 2);
+
+ // Now increment membuf references
+ Packet::MemBufPtr membuf = packet.acquireMemBufReference();
+
+ // Now reference count should be 2
+ EXPECT_EQ(membuf_ref.use_count(), 3);
+
+ // Copy again
+ Packet::MemBufPtr membuf2 = membuf;
+
+ // Now reference count should be 3
+ EXPECT_EQ(membuf_ref.use_count(), 4);
+}
+
+TEST_F(PacketTest, TestReset) {
+ // Check everything is ok
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP);
+ EXPECT_EQ(packet.length(), raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+ EXPECT_EQ(packet.headerSize(), IPV6_HDRLEN + TCP_HDRLEN);
+ EXPECT_EQ(packet.payloadSize(), packet.length() - packet.headerSize());
+
+ // Reset the packet
+ packet.reset();
+
+ // Rerun test
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_NONE);
+ EXPECT_EQ(packet.length(), std::size_t(0));
+ EXPECT_EQ(packet.headerSize(), std::size_t(0));
+ EXPECT_EQ(packet.payloadSize(), std::size_t(0));
+}
+
+TEST_F(PacketTest, TestAppendPayload) {
+ // Append payload with raw buffer
+ uint8_t raw_buffer[2048];
+ auto original_payload_length = packet.payloadSize();
+ packet.appendPayload(raw_buffer, 1024);
+
+ EXPECT_EQ(original_payload_length + 1024, packet.payloadSize());
+
+ for (int i = 0; i < 10; i++) {
+ // Append other payload 10 times
+ packet.appendPayload(raw_buffer, 1024);
+ EXPECT_EQ(original_payload_length + 1024 + (1024) * (i + 1),
+ packet.payloadSize());
+ }
+
+ // Append payload using membuf
+ packet.appendPayload(utils::MemBuf::copyBuffer(raw_buffer, 2048));
+ EXPECT_EQ(original_payload_length + 1024 + 1024 * 10 + 2048,
+ packet.payloadSize());
+
+ // Check the underlying MemBuf length is the expected one
+ utils::MemBuf *current = &packet;
+ size_t total = 0;
+ do {
+ total += current->length();
+ current = current->next();
+ } while (current != &packet);
+
+ EXPECT_EQ(total, packet.headerSize() + packet.payloadSize());
+
+ // LEt's try now to reset this packet
+ packet.reset();
+
+ // There should be no more bufferls left in the chain
+ EXPECT_EQ(&packet, packet.next());
+ EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_NONE);
+ EXPECT_EQ(packet.length(), std::size_t(0));
+ EXPECT_EQ(packet.headerSize(), std::size_t(0));
+ EXPECT_EQ(packet.payloadSize(), std::size_t(0));
+}
+
+TEST_F(PacketTest, GetPayload) {
+ // Append payload with raw buffer
+ uint8_t raw_buffer[2048];
+ memset(raw_buffer, 0, sizeof(raw_buffer));
+ auto original_payload_length = packet.payloadSize();
+ packet.appendPayload(raw_buffer, 2048);
+
+ // Get payload
+ auto payload = packet.getPayload();
+ // Check payload length is correct
+ utils::MemBuf *current = payload.get();
+ size_t total = 0;
+ do {
+ total += current->length();
+ current = current->next();
+ } while (current != payload.get());
+
+ ASSERT_EQ(total, packet.payloadSize());
+
+ // Linearize the payload
+ payload->gather(total);
+
+ // Check memory correspond
+ payload->trimStart(original_payload_length);
+ auto ret = memcmp(raw_buffer, payload->data(), 2048);
+ EXPECT_EQ(ret, 0);
+}
+
+TEST_F(PacketTest, UpdateLength) {
+ auto original_payload_size = packet.payloadSize();
+
+ // Add some fake payload without using the API
+ packet.append(200);
+
+ // payloadSize does not know about the new payload, yet
+ EXPECT_EQ(packet.payloadSize(), original_payload_size);
+
+ // Let's now update the packet length
+ packet.updateLength();
+
+ // Now payloadSize knows
+ EXPECT_EQ(packet.payloadSize(), std::size_t(original_payload_size + 200));
+
+ // We may also update the length without adding real content. This is only
+ // written in the packet header.
+ packet.updateLength(128);
+ EXPECT_EQ(packet.payloadSize(),
+ std::size_t(original_payload_size + 200 + 128));
+}
+
+TEST_F(PacketTest, SetGetPayloadType) {
+ auto payload_type = packet.getPayloadType();
+
+ // It should be normal content object by default
+ EXPECT_EQ(payload_type, PayloadType::DATA);
+
+ // Set it to be manifest
+ packet.setPayloadType(PayloadType::MANIFEST);
+
+ // Check it is manifest
+ payload_type = packet.getPayloadType();
+
+ EXPECT_EQ(payload_type, PayloadType::MANIFEST);
+}
+
+TEST_F(PacketTest, GetFormat) {
+ {
+ PacketForTest p0(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP, p0);
+
+ PacketForTest p1(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP, p1);
+
+ PacketForTest p2(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_ICMP, p2);
+
+ PacketForTest p3(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_ICMP, p3);
+
+ PacketForTest p4(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH, p4);
+
+ PacketForTest p5(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH].size());
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH, p5);
+ }
+
+ // Let's try now creating empty packets
+ {
+ PacketForTest p0(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP);
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP, p0);
+
+ PacketForTest p1(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP);
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP, p1);
+
+ PacketForTest p2(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_ICMP);
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_ICMP, p2);
+
+ PacketForTest p3(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_ICMP);
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_ICMP, p3);
+
+ PacketForTest p4(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP_AH);
+ testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH, p4);
+
+ PacketForTest p5(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH, p5);
+ }
+}
+
+TEST_F(PacketTest, SetGetTestSignatureTimestamp) {
+ // Let's try to set the signature timestamp in a packet without AH header. We
+ // expect an exception.
+ using namespace std::chrono;
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ try {
+ packet.setSignatureTimestamp(now);
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Same fot get method
+ try {
+ auto t = packet.getSignatureTimestamp();
+ // Let's make compiler happy
+ (void)t;
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Now let's construct a AH packet, with no additional space for signature
+ PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ p.setSignatureTimestamp(now);
+ uint64_t now_get = p.getSignatureTimestamp();
+
+ // Check we got the right value
+ EXPECT_EQ(now_get, now);
+}
+
+TEST_F(PacketTest, TestSetGetValidationAlgorithm) {
+ // Let's try to set the validation algorithm in a packet without AH header. We
+ // expect an exception.
+
+ try {
+ packet.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256);
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Same fot get method
+ try {
+ auto v = packet.getSignatureTimestamp();
+ // Let's make compiler happy
+ (void)v;
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Now let's construct a AH packet, with no additional space for signature
+ PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ p.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256);
+ auto v_get = p.getValidationAlgorithm();
+
+ // Check we got the right value
+ EXPECT_EQ(v_get, auth::CryptoSuite::RSA_SHA256);
+}
+
+TEST_F(PacketTest, TestSetGetKeyId) {
+ uint8_t key[32];
+ memset(key, 0, sizeof(key));
+ auth::KeyId key_id = std::make_pair(key, sizeof(key));
+
+ try {
+ packet.setKeyId(key_id);
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Same for get method
+ try {
+ auto k = packet.getKeyId();
+ // Let's make compiler happy
+ (void)k;
+ FAIL() << "We should not reach this point.";
+ } catch (const errors::RuntimeException &exc) {
+ /* ok right exception*/
+ } catch (...) {
+ FAIL() << "Unexpected exception";
+ }
+
+ // Now let's construct a AH packet, with no additional space for signature
+ PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH);
+ p.setKeyId(key_id);
+ auto p_get = p.getKeyId();
+
+ // Check we got the right value
+ EXPECT_EQ(p_get.second, key_id.second);
+
+ auto ret = memcmp(p_get.first, key_id.first, p_get.second);
+ EXPECT_EQ(ret, 0);
+}
+
+TEST_F(PacketTest, DISABLED_TestChecksum) {
+ // Checksum should be wrong
+ bool integrity = packet.checkIntegrity();
+ EXPECT_FALSE(integrity);
+
+ // Let's fix it
+ packet.setChecksum();
+
+ // Check again
+ integrity = packet.checkIntegrity();
+ EXPECT_TRUE(integrity);
+
+ // Check with AH header and 300 bytes signature
+ PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH,
+ 300);
+ std::string payload(5000, 'X');
+ p.appendPayload((const uint8_t *)payload.c_str(), payload.size() / 2);
+ p.appendPayload((const uint8_t *)(payload.c_str() + payload.size() / 2),
+ payload.size() / 2);
+
+ p.setChecksum();
+ integrity = p.checkIntegrity();
+ EXPECT_TRUE(integrity);
+}
+
+TEST_F(PacketTest, TestEnsureCapacity) {
+ PacketForTest &p = packet;
+
+ // This shoul be false
+ auto ret =
+ p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() + 10);
+ EXPECT_FALSE(ret);
+
+ // This should be true
+ ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+ EXPECT_TRUE(ret);
+
+ // This should be true
+ ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - 10);
+ EXPECT_TRUE(ret);
+
+ // Try to trim the packet start
+ p.trimStart(10);
+ // Now this should be false
+ ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+ EXPECT_FALSE(ret);
+
+ // Create a new packet
+ auto p2 = PacketForTest(Packet::WRAP_BUFFER,
+ &raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size(),
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size());
+
+ p2.appendPayload(utils::MemBuf::createCombined(2000));
+
+ // This should be false, since the buffer is chained
+ ret =
+ p2.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - 10);
+ EXPECT_FALSE(ret);
+}
+
+//
+// This test is disabled as it manipulates a ipv6 header with the wrong payload
+// length inside.
+//
+TEST_F(PacketTest, DISABLED_TestEnsureCapacityAndFillUnused) {
+ // Create packet by excluding the payload (So only L3 + L4 headers). The
+ // payload will be trated as unused tailroom
+ PacketForTest p = PacketForTest(
+ Packet::WRAP_BUFFER, &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0],
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - PAYLOAD_SIZE,
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size());
+
+ // Copy original packet payload, which is here trated as a unused tailroom
+ uint8_t original_payload[PAYLOAD_SIZE];
+ uint8_t *payload = &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0] +
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() -
+ PAYLOAD_SIZE;
+ std::memcpy(original_payload, payload, PAYLOAD_SIZE);
+
+ // This should be true and the unused tailroom should be unmodified
+ auto ret = p.ensureCapacityAndFillUnused(
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - (PAYLOAD_SIZE + 10),
+ 0);
+ EXPECT_TRUE(ret);
+ ret = std::memcmp(original_payload, payload, PAYLOAD_SIZE);
+ EXPECT_EQ(ret, 0);
+
+ // This should fill the payload with zeros
+ ret = p.ensureCapacityAndFillUnused(
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(), 0);
+ EXPECT_TRUE(ret);
+ uint8_t zeros[PAYLOAD_SIZE];
+ std::memset(zeros, 0, PAYLOAD_SIZE);
+ ret = std::memcmp(payload, zeros, PAYLOAD_SIZE);
+ EXPECT_EQ(ret, 0);
+
+ // This should fill the payload with ones
+ ret = p.ensureCapacityAndFillUnused(
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(), 1);
+ EXPECT_TRUE(ret);
+ uint8_t ones[PAYLOAD_SIZE];
+ std::memset(ones, 1, PAYLOAD_SIZE);
+ ret = std::memcmp(payload, ones, PAYLOAD_SIZE);
+ EXPECT_EQ(ret, 0);
+
+ // This should return false and the payload should be unmodified
+ ret = p.ensureCapacityAndFillUnused(
+ raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() + 1, 1);
+ EXPECT_FALSE(ret);
+ ret = std::memcmp(payload, ones, PAYLOAD_SIZE);
+ EXPECT_EQ(ret, 0);
+}
+
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_packet_allocator.cc b/libtransport/src/test/test_packet_allocator.cc
new file mode 100644
index 000000000..0de35a817
--- /dev/null
+++ b/libtransport/src/test/test_packet_allocator.cc
@@ -0,0 +1,132 @@
+/*
+ * 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 <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/interest.h>
+#define ALLOCATION_CHECKS
+#include <hicn/transport/core/global_object_pool.h>
+#undef ALLOCATION_CHECKS
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <hicn/transport/utils/event_thread.h>
+
+namespace transport {
+namespace core {
+
+class PacketAllocatorTest : public ::testing::Test {
+ protected:
+ static inline const std::size_t default_size = 2048;
+ static inline const std::size_t default_n_buffer = 1024;
+ static inline const std::size_t counter = 1024;
+ static inline const std::size_t total_packets = 1024 * counter;
+
+ // Get fixed block allocator_ of 1024 buffers of size 2048 bytes
+ PacketAllocatorTest() : allocator_(PacketManager<>::getInstance()) {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~PacketAllocatorTest() {
+ // 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() {}
+
+ virtual void TearDown() {}
+
+ static bool pointerIsAligned(const void *pointer, size_t byte_count) {
+ return uintptr_t(pointer) % byte_count == 0;
+ }
+
+ template <typename T, typename... Args>
+ void allocationTest(Args &&...args) {
+ // Create packet
+ auto packet = allocator_.getPacket<T>(std::forward<Args>(args)...);
+
+ // Check boundaries
+ LOG(INFO) << "packet size: " << sizeof(*packet) + sizeof(packet)
+ << std::endl;
+ EXPECT_LE(sizeof(*packet) + sizeof(packet) + sizeof(std::max_align_t),
+ sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr));
+ }
+
+ PacketManager<> &allocator_;
+};
+
+TEST_F(PacketAllocatorTest, ContentObjectAllocation) {
+ allocationTest<core::ContentObject>(HICN_PACKET_FORMAT_IPV4_TCP);
+}
+
+TEST_F(PacketAllocatorTest, InterestAllocation) {
+ allocationTest<core::Interest>(HICN_PACKET_FORMAT_IPV4_TCP);
+}
+
+// TEST_F(PacketAllocatorTest, MemBufAllocation) {
+// allocationTest<::utils::MemBuf>();
+// }
+
+TEST_F(PacketAllocatorTest, CheckAllocationIsCorrect) {
+ // Create packet
+ auto packet =
+ allocator_.getPacket<core::ContentObject>(HICN_PACKET_FORMAT_IPV4_TCP);
+
+ // Address of actual buffer
+ uint8_t *buffer_address = packet->writableData();
+
+ // Address of packet
+ uint8_t *packet_address = reinterpret_cast<uint8_t *>(packet.get());
+
+ uint8_t *start_address =
+ buffer_address -
+ sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr);
+
+ // Check memory was allocated on correct positions
+ EXPECT_TRUE(pointerIsAligned(start_address, alignof(std::max_align_t)));
+ EXPECT_TRUE(packet_address > start_address &&
+ packet_address < buffer_address);
+ EXPECT_TRUE(pointerIsAligned(buffer_address, alignof(std::max_align_t)));
+ EXPECT_THAT(std::size_t(buffer_address - start_address),
+ testing::Eq(sizeof(
+ PacketManager<>::PacketStorage::packet_and_shared_ptr)));
+}
+
+TEST_F(PacketAllocatorTest, CheckAllocationSpeed) {
+ // Check time needed to allocate 1 million packeauto &packet_manager =
+ auto &packet_manager = core::PacketManager<>::getInstance();
+
+ // Send 1 million packets
+ std::array<utils::MemBuf::Ptr, counter> packets;
+ auto t0 = utils::SteadyTime::now();
+ std::size_t sum = 0;
+ for (std::size_t j = 0; j < counter; j++) {
+ for (std::size_t i = 0; i < counter; i++) {
+ packets[i] = packet_manager.getMemBuf();
+ sum++;
+ }
+ }
+ auto t1 = utils::SteadyTime::now();
+
+ auto delta = utils::SteadyTime::getDurationUs(t0, t1);
+ auto rate = double(sum) * 1000000.0 / double(delta.count());
+
+ LOG(INFO) << "rate: " << rate << " packets/s";
+}
+
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_prefix.cc b/libtransport/src/test/test_prefix.cc
new file mode 100644
index 000000000..3eab72bcb
--- /dev/null
+++ b/libtransport/src/test/test_prefix.cc
@@ -0,0 +1,334 @@
+/*
+ * 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 <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/core/prefix.h>
+#include <hicn/transport/errors/invalid_ip_address_exception.h>
+#include <hicn/transport/portability/endianess.h>
+
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace transport {
+namespace core {
+
+namespace {
+class PrefixTest : public ::testing::Test {
+ protected:
+ static inline const char prefix_str0[] = "2001:db8:1::/64";
+ static inline const char prefix_str1[] = "10.11.12.0/24";
+ static inline const char prefix_str2[] = "2001:db8:1::abcd/64";
+ static inline const char prefix_str3[] = "10.11.12.245/27";
+ static inline const char wrong_prefix_str0[] = "10.11.12.245/45";
+ static inline const char wrong_prefix_str1[] = "10.400.12.13/8";
+ static inline const char wrong_prefix_str2[] = "2001:db8:1::/640";
+ static inline const char wrong_prefix_str3[] = "20011::db8:1::/16";
+ static inline const char wrong_prefix_str4[] = "2001::db8:1::fffff/96";
+
+ PrefixTest() = default;
+
+ ~PrefixTest() override = default;
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ void SetUp() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+TEST_F(PrefixTest, ConstructorRightString) {
+ // Create empty prefix
+ Prefix p;
+
+ // Create prefix from string
+ Prefix p0(prefix_str0);
+ // Reconstruct string and check it is equal to original address
+ std::string network = p0.getNetwork();
+ std::uint16_t prefix_length = p0.getPrefixLength();
+ EXPECT_THAT(network + "/" + std::to_string(prefix_length),
+ ::testing::StrEq(prefix_str0));
+
+ // Create prefix from string
+ Prefix p1(prefix_str1);
+ // Reconstruct string and check it is equal to original address
+ network = p1.getNetwork();
+ prefix_length = p1.getPrefixLength();
+ EXPECT_THAT(network + "/" + std::to_string(prefix_length),
+ ::testing::StrEq(prefix_str1));
+
+ // Create prefix from string
+ Prefix p2(prefix_str2);
+ // Reconstruct string and check it is equal to original address
+ network = p2.getNetwork();
+ prefix_length = p2.getPrefixLength();
+ EXPECT_THAT(network + "/" + std::to_string(prefix_length),
+ ::testing::StrEq(prefix_str2));
+
+ // Create prefix from string
+ Prefix p3(prefix_str3);
+ // Reconstruct string and check it is equal to original address
+ network = p3.getNetwork();
+ prefix_length = p3.getPrefixLength();
+ EXPECT_THAT(network + "/" + std::to_string(prefix_length),
+ ::testing::StrEq(prefix_str3));
+
+ // Create prefix from string and prefix length
+ Prefix p4("2001::1234", 66);
+ // Reconstruct string and check it is equal to original address
+ network = p4.getNetwork();
+ prefix_length = p4.getPrefixLength();
+ auto af = p4.getAddressFamily();
+ EXPECT_THAT(network, ::testing::StrEq("2001::1234"));
+ EXPECT_THAT(prefix_length, ::testing::Eq(66));
+ EXPECT_THAT(af, ::testing::Eq(AF_INET6));
+}
+
+TEST_F(PrefixTest, ConstructorWrongString) {
+ try {
+ Prefix p0(wrong_prefix_str0);
+ FAIL() << "Expected exception";
+ } catch (const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+
+ try {
+ Prefix p1(wrong_prefix_str1);
+ FAIL() << "Expected exception";
+ } catch (const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+
+ try {
+ Prefix p2(wrong_prefix_str2);
+ FAIL() << "Expected exception";
+ } catch (const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+
+ try {
+ Prefix p3(wrong_prefix_str3);
+ FAIL() << "Expected exception";
+ } catch (const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+
+ try {
+ Prefix p4(wrong_prefix_str4);
+ FAIL() << "Expected exception";
+ } catch (const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+}
+
+TEST_F(PrefixTest, Comparison) {
+ Prefix p0(prefix_str0);
+ Prefix p1(prefix_str1);
+
+ // Expect they are different
+ EXPECT_THAT(p0, ::testing::Ne(p1));
+
+ auto p2 = p1;
+ // Expect they are equal
+ EXPECT_THAT(p1, ::testing::Eq(p2));
+}
+
+TEST_F(PrefixTest, ToSockAddress) {
+ Prefix p0(prefix_str3);
+
+ auto ret = p0.toSockaddr();
+ auto sockaddr = reinterpret_cast<sockaddr_in *>(ret.get());
+
+ EXPECT_THAT(sockaddr->sin_family, ::testing::Eq(AF_INET));
+ EXPECT_THAT(sockaddr->sin_addr.s_addr, portability::host_to_net(0x0a0b0cf5));
+}
+
+TEST_F(PrefixTest, GetPrefixLength) {
+ Prefix p0(prefix_str3);
+ EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(27));
+}
+
+TEST_F(PrefixTest, SetPrefixLength) {
+ Prefix p0(prefix_str3);
+ EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(27));
+ p0.setPrefixLength(20);
+ EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(20));
+
+ try {
+ p0.setPrefixLength(33);
+ FAIL() << "Expected exception";
+ } catch ([[maybe_unused]] const errors::InvalidIpAddressException &) {
+ // Expected exception
+ }
+}
+
+TEST_F(PrefixTest, SetGetNetwork) {
+ Prefix p0(prefix_str0);
+ EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(64));
+ p0.setNetwork("b001::1234");
+ EXPECT_THAT(p0.getNetwork(), ::testing::StrEq("b001::1234"));
+ EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(64));
+}
+
+TEST_F(PrefixTest, Contains) {
+ // IPv6 prefix
+ Prefix p0(prefix_str0);
+ hicn_ip_address_t ip0, ip1;
+
+ hicn_ip_address_pton("2001:db8:1::1234", &ip0);
+ hicn_ip_address_pton("2001:db9:1::1234", &ip1);
+
+ EXPECT_TRUE(p0.contains(ip0));
+ EXPECT_FALSE(p0.contains(ip1));
+
+ Prefix p1(prefix_str1);
+ hicn_ip_address_pton("10.11.12.12", &ip0);
+ hicn_ip_address_pton("10.12.12.13", &ip1);
+
+ EXPECT_TRUE(p1.contains(ip0));
+ EXPECT_FALSE(p1.contains(ip1));
+
+ Prefix p2(prefix_str2);
+ hicn_ip_address_pton("2001:db8:1::dbca", &ip0);
+ hicn_ip_address_pton("10.12.12.12", &ip1);
+
+ EXPECT_TRUE(p2.contains(ip0));
+ EXPECT_FALSE(p2.contains(ip1));
+
+ Prefix p3(prefix_str3);
+ hicn_ip_address_pton("10.11.12.245", &ip0);
+ hicn_ip_address_pton("10.11.12.1", &ip1);
+
+ EXPECT_TRUE(p3.contains(ip0));
+ EXPECT_FALSE(p3.contains(ip1));
+
+ // Corner cases
+ Prefix p4("::/0");
+ hicn_ip_address_pton("7001:db8:1::1234", &ip0);
+ hicn_ip_address_pton("8001:db8:1::1234", &ip1);
+
+ EXPECT_TRUE(p4.contains(ip0));
+ EXPECT_TRUE(p4.contains(ip1));
+
+ // Corner cases
+ Prefix p5("b001:a:b:c:d:e:f:1/128");
+ hicn_ip_address_pton("b001:a:b:c:d:e:f:1", &ip0);
+ hicn_ip_address_pton("b001:a:b:c:d:e:f:2", &ip1);
+
+ EXPECT_TRUE(p5.contains(ip0));
+ EXPECT_FALSE(p5.contains(ip1));
+}
+
+TEST_F(PrefixTest, GetAddressFamily) {
+ Prefix p0(prefix_str0);
+ auto af = p0.getAddressFamily();
+ EXPECT_THAT(af, ::testing::Eq(AF_INET6));
+
+ Prefix p1(prefix_str1);
+ af = p1.getAddressFamily();
+ EXPECT_THAT(af, ::testing::Eq(AF_INET));
+}
+
+TEST_F(PrefixTest, MakeName) {
+ Prefix p0(prefix_str0);
+ auto name0 = p0.makeName();
+ EXPECT_THAT(name0.toString(), ::testing::StrEq("2001:db8:1::|0"));
+
+ Prefix p1(prefix_str1);
+ auto name1 = p1.makeName();
+ EXPECT_THAT(name1.toString(), ::testing::StrEq("10.11.12.0|0"));
+
+ Prefix p2(prefix_str2);
+ auto name2 = p2.makeName();
+ EXPECT_THAT(name2.toString(), ::testing::StrEq("2001:db8:1::|0"));
+
+ Prefix p3(prefix_str3);
+ auto name3 = p3.makeName();
+ EXPECT_THAT(name3.toString(), ::testing::StrEq("10.11.12.224|0"));
+
+ Prefix p4("b001:a:b:c:d:e:f:1/128");
+ auto name4 = p4.makeName();
+ EXPECT_THAT(name4.toString(), ::testing::StrEq("b001:a:b:c:d:e:f:1|0"));
+}
+
+TEST_F(PrefixTest, MakeRandomName) {
+ Prefix p0(prefix_str0);
+ auto name0 = p0.makeRandomName();
+ auto name1 = p0.makeRandomName();
+ auto name2 = p0.makeRandomName();
+ auto name3 = p0.makeRandomName();
+
+ EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name1)));
+ EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name2)));
+ EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name3)));
+ EXPECT_THAT(name1, ::testing::Not(::testing::Eq(name2)));
+ EXPECT_THAT(name1, ::testing::Not(::testing::Eq(name3)));
+ EXPECT_THAT(name2, ::testing::Not(::testing::Eq(name3)));
+
+ // Corner case
+ Prefix p2("b001:a:b:c:d:e:f:1/128");
+ name0 = p2.makeRandomName();
+ name1 = p2.makeRandomName();
+ name2 = p2.makeRandomName();
+ name3 = p2.makeRandomName();
+
+ EXPECT_THAT(name0, ::testing::Eq(name1));
+ EXPECT_THAT(name0, ::testing::Eq(name2));
+ EXPECT_THAT(name0, ::testing::Eq(name3));
+ EXPECT_THAT(name1, ::testing::Eq(name2));
+ EXPECT_THAT(name1, ::testing::Eq(name3));
+ EXPECT_THAT(name2, ::testing::Eq(name3));
+}
+
+TEST_F(PrefixTest, MakeNameWithIndex) {
+ Prefix p0(prefix_str0);
+ auto name0 = p0.makeNameWithIndex(0);
+ EXPECT_THAT(name0.toString(), ::testing::StrEq("2001:db8:1::|0"));
+ auto name1 = p0.makeNameWithIndex(1);
+ EXPECT_THAT(name1.toString(), ::testing::StrEq("2001:db8:1::1|0"));
+ auto name2 = p0.makeNameWithIndex(2);
+ EXPECT_THAT(name2.toString(), ::testing::StrEq("2001:db8:1::2|0"));
+ auto name3 = p0.makeNameWithIndex(3);
+ EXPECT_THAT(name3.toString(), ::testing::StrEq("2001:db8:1::3|0"));
+
+ Prefix p1(prefix_str1);
+ name0 = p1.makeNameWithIndex(0);
+ EXPECT_THAT(name0.toString(), ::testing::StrEq("10.11.12.0|0"));
+ name1 = p1.makeNameWithIndex(1);
+ EXPECT_THAT(name1.toString(), ::testing::StrEq("10.11.12.1|0"));
+ name2 = p1.makeNameWithIndex(2);
+ EXPECT_THAT(name2.toString(), ::testing::StrEq("10.11.12.2|0"));
+ name3 = p1.makeNameWithIndex(3);
+ EXPECT_THAT(name3.toString(), ::testing::StrEq("10.11.12.3|0"));
+
+ // Test truncation
+ Prefix p2("b001::/96");
+ name0 = p2.makeNameWithIndex(0xffffffffffffffff);
+ EXPECT_THAT(name0.toString(), ::testing::StrEq("b001::ffff:ffff|0"));
+}
+
+} // namespace
+
+} // namespace core
+} // namespace transport
diff --git a/libtransport/src/test/test_quadloop.cc b/libtransport/src/test/test_quadloop.cc
new file mode 100644
index 000000000..6a08033aa
--- /dev/null
+++ b/libtransport/src/test/test_quadloop.cc
@@ -0,0 +1,176 @@
+/*
+ * 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 <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/portability/cache.h>
+
+#include <array>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace utils {
+
+class LoopTest : public ::testing::Test {
+ protected:
+ static inline const std::size_t size = 256;
+
+ LoopTest() = default;
+
+ ~LoopTest() override = default;
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ void SetUp() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+// 1 cache line struct (64 bytes)
+struct Data {
+ std::array<uint64_t, 8> data;
+};
+
+TEST_F(LoopTest, QuadLoopTest) {
+ // Create 2 arrays of 256 elements
+ std::vector<std::unique_ptr<Data>> _from;
+ std::vector<std::unique_ptr<Data>> _to_next;
+ _from.reserve(size);
+ _to_next.reserve(size);
+
+ int n_left_from = size;
+ int n_left_to_next = size;
+
+ // Initialize the arrays
+ for (std::size_t i = 0; i < size; i++) {
+ _from.push_back(std::make_unique<Data>());
+ _to_next.push_back(std::make_unique<Data>());
+
+ for (int j = 0; j < 8; j++) {
+ _from[i]->data[j] = j;
+ _to_next[i]->data[j] = 0;
+ }
+ }
+
+ const std::unique_ptr<Data> *from = &_from[0];
+ const std::unique_ptr<Data> *to_next = &_to_next[0];
+
+ clock_t start;
+ clock_t end;
+ double clocks;
+
+ start = clock();
+ // Create a quad loop
+ while (n_left_from > 0) {
+ while (n_left_from >= 4 && n_left_to_next >= 4) {
+ {
+ using namespace transport::portability::cache;
+ Data *d2;
+ Data *d3;
+
+ d2 = from[2].get();
+ d3 = from[3].get();
+
+ prefetch<Data, READ>(d2, sizeof(Data));
+ prefetch<Data, READ>(d3, sizeof(Data));
+
+ d2 = to_next[2].get();
+ d3 = to_next[3].get();
+
+ prefetch<Data, WRITE>(d2, sizeof(Data));
+ prefetch<Data, WRITE>(d3, sizeof(Data));
+ }
+
+ // Do 4 iterations
+ std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(),
+ sizeof(Data));
+ std::memcpy(to_next[1].get()->data.data(), from[1].get()->data.data(),
+ sizeof(Data));
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+ from += 2;
+ to_next += 2;
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(),
+ sizeof(Data));
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+ from += 1;
+ to_next += 1;
+ }
+ }
+ end = clock();
+ clocks = (double)(end - start);
+
+ LOG(INFO) << "Time with quad loop: " << clocks << std::endl;
+}
+
+TEST_F(LoopTest, NormalLoopTest) {
+ // Create 2 arrays of 256 elements
+ std::vector<std::unique_ptr<Data>> _from;
+ std::vector<std::unique_ptr<Data>> _to_next;
+ _from.reserve(size);
+ _to_next.reserve(size);
+
+ int n_left_from = size;
+ int n_left_to_next = size;
+
+ // Initialize the arrays
+ for (std::size_t i = 0; i < size; i++) {
+ _from.push_back(std::make_unique<Data>());
+ _to_next.push_back(std::make_unique<Data>());
+
+ for (int j = 0; j < 8; j++) {
+ _from[i]->data[j] = j;
+ _to_next[i]->data[j] = 0;
+ }
+ }
+
+ const std::unique_ptr<Data> *from = &_from[0];
+ const std::unique_ptr<Data> *to_next = &_to_next[0];
+
+ clock_t start;
+ clock_t end;
+ double clocks;
+
+ start = clock();
+ while (n_left_from > 0) {
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(),
+ sizeof(Data));
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+ from += 1;
+ to_next += 1;
+ }
+ }
+ end = clock();
+ clocks = ((double)(end - start));
+
+ LOG(INFO) << "Time with normal loop: " << clocks << std::endl;
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/test/test_quality_score.cc b/libtransport/src/test/test_quality_score.cc
new file mode 100644
index 000000000..9513c94a6
--- /dev/null
+++ b/libtransport/src/test/test_quality_score.cc
@@ -0,0 +1,135 @@
+/*
+ * 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 <hicn/transport/utils/rtc_quality_score.h>
+
+#include <climits>
+#include <random>
+#include <vector>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+TEST(QualityScoreTest, testQS) {
+ RTCQualityScore qs;
+ uint8_t score;
+
+ // 0 losses
+ score = qs.getQualityScore(0, 0);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(98, 0);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(188, 0);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(398, 0);
+ EXPECT_EQ(score, (uint8_t)4);
+
+ score = qs.getQualityScore(400, 0);
+ EXPECT_EQ(score, (uint8_t)3);
+
+ score = qs.getQualityScore(598, 0);
+ EXPECT_EQ(score, (uint8_t)3);
+
+ score = qs.getQualityScore(600, 0);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ score = qs.getQualityScore(700, 0);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ score = qs.getQualityScore(50000, 0);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ // 0 delay
+ score = qs.getQualityScore(0, 2);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(0, 9);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(0, 29);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(0, 30);
+ EXPECT_EQ(score, (uint8_t)4);
+
+ score = qs.getQualityScore(0, 39);
+ EXPECT_EQ(score, (uint8_t)4);
+
+ score = qs.getQualityScore(0, 40);
+ EXPECT_EQ(score, (uint8_t)3);
+
+ score = qs.getQualityScore(0, 50);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ score = qs.getQualityScore(0, 5000);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ // loss < 10
+ score = qs.getQualityScore(0, 3);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(98, 9);
+ EXPECT_EQ(score, (uint8_t)4);
+
+ score = qs.getQualityScore(100, 9);
+ EXPECT_EQ(score, (uint8_t)3);
+
+ score = qs.getQualityScore(398, 5);
+ EXPECT_EQ(score, (uint8_t)2);
+
+ score = qs.getQualityScore(400, 5);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ score = qs.getQualityScore(4000, 5);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ // loss < 20
+ score = qs.getQualityScore(0, 10);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(30, 10);
+ EXPECT_EQ(score, (uint8_t)3);
+
+ score = qs.getQualityScore(198, 15);
+ EXPECT_EQ(score, (uint8_t)2);
+
+ score = qs.getQualityScore(200, 19);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ score = qs.getQualityScore(300, 10);
+ EXPECT_EQ(score, (uint8_t)1);
+
+ // loss < 30
+
+ score = qs.getQualityScore(0, 29);
+ EXPECT_EQ(score, (uint8_t)5);
+
+ score = qs.getQualityScore(10, 29);
+ EXPECT_EQ(score, (uint8_t)2);
+
+ score = qs.getQualityScore(0, 100);
+ EXPECT_EQ(score, (uint8_t)1);
+}
+
+} // namespace rtc
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/test/test_sessions.cc b/libtransport/src/test/test_sessions.cc
new file mode 100644
index 000000000..d2e8c27bb
--- /dev/null
+++ b/libtransport/src/test/test_sessions.cc
@@ -0,0 +1,77 @@
+/*
+ * 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 <hicn/transport/interfaces/global_conf_interface.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_options_keys.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+
+namespace transport {
+namespace interface {
+
+class SessionsTest : public ::testing::Test {
+ protected:
+ static inline const std::size_t default_size = 2048;
+ static inline const std::size_t default_n_buffer = 1024;
+
+ // Get fixed block allocator_ of 1024 buffers of size 2048 bytes
+ SessionsTest() {
+ // You can do set-up work for each test here.
+ // Set io_module to local forwarder with no external connections
+ global_config::IoModuleConfiguration config;
+ config.name = "forwarder_module";
+ config.set();
+ }
+
+ virtual ~SessionsTest() {
+ // 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).
+ }
+
+ std::vector<ConsumerSocket> consumers_;
+ std::vector<ProducerSocket> producers_;
+};
+
+TEST_F(SessionsTest, SessionAllocations) {
+ // Create 1000 consumer sockets and 1000 producer sockets
+ int cprotocol = TransportProtocolAlgorithms::RAAQM;
+ int pprotocol = ProductionProtocolAlgorithms::BYTE_STREAM;
+ int offset = 0;
+
+ for (int i = 0; i < 1000; i++) {
+ auto &c = consumers_.emplace_back(cprotocol + (offset % 3));
+ auto &p = producers_.emplace_back(pprotocol + (offset % 2));
+ c.connect();
+ p.connect();
+ offset++;
+ }
+}
+
+} // namespace interface
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/test/test_thread_pool.cc b/libtransport/src/test/test_thread_pool.cc
new file mode 100644
index 000000000..1b6b4cc81
--- /dev/null
+++ b/libtransport/src/test/test_thread_pool.cc
@@ -0,0 +1,70 @@
+/*
+ * 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 <hicn/transport/utils/thread_pool.h>
+
+namespace utils {
+
+class ThreadPoolTest : public ::testing::Test {
+ protected:
+ ThreadPoolTest() : thread_pool_() {
+ // You can do set-up work for each test here.
+ }
+
+ virtual ~ThreadPoolTest() {
+ // 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).
+ }
+
+ ::utils::ThreadPool thread_pool_;
+};
+
+TEST_F(ThreadPoolTest, DefaultConstructor) {
+ // EXPECT_EQ(thread_pool_.GetNumThreads(), 0);
+ // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0);
+ // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0);
+}
+
+TEST_F(ThreadPoolTest, GetNThreads) {
+ auto n_threads = thread_pool_.getNThreads();
+ EXPECT_GT(n_threads, std::size_t(0));
+ EXPECT_EQ(n_threads, std::thread::hardware_concurrency());
+
+ ::utils::ThreadPool pool(64);
+ n_threads = pool.getNThreads();
+ EXPECT_GT(n_threads, std::size_t(0));
+ EXPECT_NE(n_threads, std::thread::hardware_concurrency());
+ EXPECT_EQ(n_threads, std::size_t(64));
+
+ // EXPECT_EQ(thread_pool_.GetNumThreads(), 0);
+ // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0);
+ // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0);
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/test/test_traffic_generator.cc b/libtransport/src/test/test_traffic_generator.cc
new file mode 100644
index 000000000..733acbb74
--- /dev/null
+++ b/libtransport/src/test/test_traffic_generator.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hicn/transport/utils/traffic_generator.h>
+
+static constexpr int NUM_PINGS = 10;
+static constexpr char PREFIX[] = "b001:1:2:3::";
+static constexpr uint32_t FIRST_SUFFIX = 5;
+
+namespace utils {
+
+using transport::IncrSuffixTrafficGenerator;
+using transport::RandomTrafficGenerator;
+
+class TrafficGeneratorTest : public ::testing::Test {
+ protected:
+ TrafficGeneratorTest() {}
+ virtual ~TrafficGeneratorTest() {}
+};
+
+TEST_F(TrafficGeneratorTest, IncrSuffixGetPrefixAndSuffix) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ std::string prefix = traffic_generator_->getPrefix();
+ EXPECT_EQ(prefix, PREFIX);
+ uint32_t suffix = traffic_generator_->getSuffix();
+ EXPECT_EQ(suffix, FIRST_SUFFIX);
+}
+
+TEST_F(TrafficGeneratorTest, IncrSuffixGetMultipleSuffixes) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ std::string prefix = traffic_generator_->getPrefix();
+ EXPECT_EQ(prefix, PREFIX);
+ EXPECT_EQ(prefix, traffic_generator_->getPrefix());
+
+ for (int i = 0; i < NUM_PINGS; i++)
+ EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX + i);
+}
+
+TEST_F(TrafficGeneratorTest, IncrSuffixReset) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ for (int i = 0; i < NUM_PINGS; i++) traffic_generator_->getSuffix();
+
+ traffic_generator_->reset();
+ EXPECT_EQ(traffic_generator_->getPrefix(), PREFIX);
+ EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX);
+ EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX + 1);
+}
+
+TEST_F(TrafficGeneratorTest, IncrSuffixRequestTooManySuffixes) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ for (int i = 0; i < NUM_PINGS; i++) traffic_generator_->getSuffix();
+ EXPECT_THROW(traffic_generator_->getSuffix(), std::runtime_error);
+}
+
+TEST_F(TrafficGeneratorTest, IncrSuffixGetPrefixAndSuffixTogether) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ auto [prefix, suffix] = traffic_generator_->getPrefixAndSuffix();
+ EXPECT_EQ(prefix, PREFIX);
+ EXPECT_EQ(suffix, FIRST_SUFFIX);
+}
+
+TEST_F(TrafficGeneratorTest, IncrSuffixCheckSentCount) {
+ auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>(
+ PREFIX, FIRST_SUFFIX, NUM_PINGS);
+
+ for (int i = 0; i < NUM_PINGS; i++) {
+ EXPECT_EQ(traffic_generator_->getSentCount(), (uint32_t)i);
+ EXPECT_FALSE(traffic_generator_->hasFinished());
+ traffic_generator_->getSuffix();
+ }
+ EXPECT_TRUE(traffic_generator_->hasFinished());
+}
+
+TEST_F(TrafficGeneratorTest, RandomGetPrefixAndSuffix) {
+ auto traffic_generator_ = std::make_unique<RandomTrafficGenerator>(NUM_PINGS);
+
+ std::string prefix1 = traffic_generator_->getPrefix();
+ std::string prefix2 = traffic_generator_->getPrefix();
+ EXPECT_NE(prefix1, prefix2);
+
+ uint32_t suffix1 = traffic_generator_->getSuffix();
+ uint32_t suffix2 = traffic_generator_->getSuffix();
+ EXPECT_NE(suffix1, suffix2);
+}
+
+TEST_F(TrafficGeneratorTest, RandomGetPrefixAndSuffixWithNetPrefix) {
+ auto traffic_generator_ = std::make_unique<RandomTrafficGenerator>(
+ NUM_PINGS, std::string(PREFIX) + "/64");
+
+ for (int i = 0; i < NUM_PINGS; i++)
+ EXPECT_THAT(traffic_generator_->getPrefix(),
+ testing::StartsWith(std::string(PREFIX)));
+}
+
+} // namespace utils \ No newline at end of file