diff options
Diffstat (limited to 'libtransport/src/test')
19 files changed, 2673 insertions, 235 deletions
diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt index 26fe7aee1..e7018ceed 100644 --- a/libtransport/src/test/CMakeLists.txt +++ b/libtransport/src/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# 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: @@ -11,42 +11,68 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(BuildMacros) - +############################################################## +# Test sources +############################################################## list(APPEND TESTS_SRC main.cc + test_aggregated_header.cc test_auth.cc - test_consumer_producer_rtc.cc + # test_consumer_producer_rtc.cc test_core_manifest.cc - test_event_thread.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 ) 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() -build_executable(unit_tests + +############################################################## +# 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 - ${LIBRARIES} - ${LIBTRANSPORT_STATIC} - ${GTEST_LIBRARIES} + ${MEMIF_MODULE_LIBRARIES} INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - ${LIBHICN_INCLUDE_DIRS} - ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + $<TARGET_PROPERTY:${LIBTRANSPORT_SHARED},INCLUDE_DIRECTORIES> ${GTEST_INCLUDE_DIRS} DEPENDS gtest ${LIBTRANSPORT_SHARED} COMPONENT ${LIBTRANSPORT_COMPONENT} - DEFINITIONS "${COMPILER_DEFINITIONS}" + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} LINK_FLAGS ${LINK_FLAGS} ) -add_test_internal(unit_tests) +add_test_internal(libtransport_tests) diff --git a/libtransport/src/test/main.cc b/libtransport/src/test/main.cc index a4d7ce1b3..591ed0d5b 100644 --- a/libtransport/src/test/main.cc +++ b/libtransport/src/test/main.cc @@ -16,7 +16,6 @@ #include <gtest/gtest.h> int main(int argc, char **argv) { - srand(time(0)); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
\ No newline at end of file diff --git a/libtransport/src/test/test_aggregated_header.cc b/libtransport/src/test/test_aggregated_header.cc new file mode 100644 index 000000000..0d88af5ab --- /dev/null +++ b/libtransport/src/test/test_aggregated_header.cc @@ -0,0 +1,622 @@ +/* + * 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 + +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]); + } +} + +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 index db1c3b52f..d7fd55433 100644 --- a/libtransport/src/test/test_auth.cc +++ b/libtransport/src/test/test_auth.cc @@ -15,10 +15,15 @@ #include <gtest/gtest.h> #include <hicn/transport/auth/crypto_hash.h> -#include <hicn/transport/auth/identity.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 { @@ -50,11 +55,23 @@ TEST_F(AuthTest, VoidVerifier) { } TEST_F(AuthTest, AsymmetricRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); + // 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(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -68,61 +85,112 @@ TEST_F(AuthTest, AsymmetricRSA) { // Create the RSA verifier std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(pubKey); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); EXPECT_EQ(signer->getSuite(), CryptoSuite::RSA_SHA256); - EXPECT_EQ(signer->getSignatureSize(), 128u); + EXPECT_EQ(signer->getSignatureSize(), 256u); EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); } TEST_F(AuthTest, AsymmetricBufferRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); + // 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::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricBufferDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // 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::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricVerifierDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // Create the DSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); - std::shared_ptr<Signer> signer = identity.getSigner(); + 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(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -134,7 +202,7 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { // EXPECT_EQ(signer->getSignatureSize(), 256u); signer->signPacket(&packet); std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(cert); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); @@ -143,33 +211,59 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { } TEST_F(AuthTest, AsymmetricBufferECDSA) { - // Create the ECDSA signer from an Identity object - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); + // 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<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); -} +} // namespace auth TEST_F(AuthTest, AsymmetricVerifierECDSA) { - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); - std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); - // Create a content object + // 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(HF_INET6_TCP_AH, signer->getSignatureSize()); diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc index 8541a9e1a..b11a6a388 100644 --- a/libtransport/src/test/test_consumer_producer_rtc.cc +++ b/libtransport/src/test/test_consumer_producer_rtc.cc @@ -41,7 +41,7 @@ class ConsumerProducerTest : public ::testing::Test, rtc_timer_(io_service_), stop_timer_(io_service_), consumer_(TransportProtocolAlgorithms::RTC, io_service_), - producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, thread_), producer_prefix_(prefix), consumer_name_(name), packets_sent_(0), @@ -120,14 +120,13 @@ class ConsumerProducerTest : public ::testing::Test, size_t maxBufferSize() const override { return receive_buffer_size; } - void readError(const std::error_code ec) noexcept override { - FAIL() << "Error while reading from RTC socket"; + 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 { packets_received_++; - std::cout << "Received something" << std::endl; } asio::io_service io_service_; diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc index 93f4e87cb..23fd5e342 100644 --- a/libtransport/src/test/test_core_manifest.cc +++ b/libtransport/src/test/test_core_manifest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * 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: @@ -17,6 +17,8 @@ #include <core/manifest_inline.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> @@ -33,7 +35,7 @@ class ManifestTest : public ::testing::Test { protected: using ContentObjectManifest = ManifestInline<ContentObject, Fixed>; - ManifestTest() : name_("b001::123|321"), manifest1_(name_) { + ManifestTest() : name_("b001::123|321"), manifest1_(HF_INET6_TCP_AH, name_) { // You can do set-up work for each test here. } @@ -97,8 +99,8 @@ TEST_F(ManifestTest, MoveConstructor) { TEST_F(ManifestTest, SetLastManifest) { manifest1_.clear(); - manifest1_.setFinalManifest(true); - bool fcn = manifest1_.isFinalManifest(); + manifest1_.setIsLast(true); + bool fcn = manifest1_.getIsLast(); ASSERT_TRUE(fcn == true); } @@ -109,13 +111,13 @@ TEST_F(ManifestTest, SetManifestType) { ManifestType type1 = ManifestType::INLINE_MANIFEST; ManifestType type2 = ManifestType::FLIC_MANIFEST; - manifest1_.setManifestType(type1); - ManifestType type_returned1 = manifest1_.getManifestType(); + manifest1_.setType(type1); + ManifestType type_returned1 = manifest1_.getType(); manifest1_.clear(); - manifest1_.setManifestType(type2); - ManifestType type_returned2 = manifest1_.getManifestType(); + manifest1_.setType(type2); + ManifestType type_returned2 = manifest1_.getType(); ASSERT_EQ(type1, type_returned1); ASSERT_EQ(type2, type_returned2); @@ -146,17 +148,80 @@ TEST_F(ManifestTest, SetHashAlgorithm) { ASSERT_EQ(hash3, type_returned3); } -TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { +TEST_F(ManifestTest, setParamsBytestream) { manifest1_.clear(); - NextSegmentCalculationStrategy strategy1 = - NextSegmentCalculationStrategy::INCREMENTAL; + ParamsBytestream params{ + .final_segment = 1, + }; + + manifest1_.setParamsBytestream(params); + manifest1_.encode(); + + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::BYTE_STREAM, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsBytestream()); +} + +TEST_F(ManifestTest, SetParamsRTC) { + manifest1_.clear(); + + ParamsRTC params{ + .timestamp = 1, + .prod_rate = 2, + .prod_seg = 3, + .support_fec = 1, + }; + + manifest1_.setParamsRTC(params); + manifest1_.encode(); - manifest1_.setNextSegmentCalculationStrategy(strategy1); - NextSegmentCalculationStrategy type_returned1 = - manifest1_.getNextSegmentCalculationStrategy(); + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::RTC_PROD, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsRTC()); +} - ASSERT_EQ(strategy1, type_returned1); +TEST_F(ManifestTest, SignManifest) { + Name name("b001::", 0); + auto signer = std::make_shared<auth::SymmetricSigner>( + auth::CryptoSuite::HMAC_SHA256, "hunter2"); + auto verifier = std::make_shared<auth::SymmetricVerifier>("hunter2"); + std::shared_ptr<ContentObjectManifest> manifest; + + // Instantiate Manifest + manifest.reset(ContentObjectManifest::createManifest( + HF_INET6_TCP_AH, name, ManifestVersion::VERSION_1, + ManifestType::INLINE_MANIFEST, false, name, signer->getHashType(), + signer->getSignatureFieldSize())); + + // Add Manifest entry + auth::CryptoHash hash(signer->getHashType()); + hash.computeDigest(std::vector<uint8_t>{0x01, 0x02, 0x03, 0x04}); + manifest->addSuffixHash(1, hash); + + // Encode manifest + manifest->encode(); + + // Sign manifest + signer->signPacket(manifest.get()); + + // Check size + ASSERT_EQ(manifest->payloadSize(), manifest->estimateManifestSize()); + ASSERT_EQ(manifest->length(), + manifest->headerSize() + manifest->payloadSize()); + ASSERT_EQ(ContentObjectManifest::manifestHeaderSize( + interface::ProductionProtocolAlgorithms::UNKNOWN), + manifest->manifestHeaderSize()); + + // Verify manifest + auth::VerificationPolicy policy = verifier->verifyPackets(manifest.get()); + ASSERT_EQ(auth::VerificationPolicy::ACCEPT, policy); } TEST_F(ManifestTest, SetBaseName) { @@ -198,23 +263,8 @@ TEST_F(ManifestTest, SetSuffixList) { } manifest1_.setBaseName(base_name); - core::Name ret_name = manifest1_.getBaseName(); - // auto & hash_list = manifest1_.getSuffixHashList(); - - // bool cond; - // int i = 0; - - // for (auto & item : manifest1_.getSuffixList()) { - // auto hash = manifest1_.getHash(suffixes[i]); - // cond = auth::CryptoHash::compareBinaryDigest(hash, - // entries[i].second.getDigest<uint8_t>().data(), - // entries[i].second.getType()); - // ASSERT_TRUE(cond); - // i++; - // } - ASSERT_EQ(base_name, ret_name); delete[] entries; diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc index 549ff9c1a..324250717 100644 --- a/libtransport/src/test/test_event_thread.cc +++ b/libtransport/src/test/test_event_thread.cc @@ -14,6 +14,7 @@ */ #include <gtest/gtest.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <hicn/transport/utils/event_thread.h> #include <cmath> @@ -48,7 +49,7 @@ class EventThreadTest : public ::testing::Test { utils::EventThread event_thread_; }; -double average(const unsigned long samples[], int size) { +inline double average(const unsigned long samples[], int size) { double sum = 0; for (int i = 0; i < size; i++) { @@ -58,7 +59,7 @@ double average(const unsigned long samples[], int size) { return sum / size; } -double stdDeviation(const unsigned long samples[], int size) { +inline double stdDeviation(const unsigned long samples[], int size) { double avg = average(samples, size); double var = 0; @@ -72,26 +73,23 @@ double stdDeviation(const unsigned long samples[], int 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++) { - auto t0 = steady_clock::now(); - event_thread_.add([t0, &samples, i]() { - auto t1 = steady_clock::now(); - samples[i] = duration_cast<nanoseconds>(t1 - t0).count(); - }); - } + // 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(); + // event_thread_.stop(); - auto avg = average(&samples[0], size); - auto sd = stdDeviation(&samples[0], size); - (void)sd; + // 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); + // // 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..41e1eae49 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rely.cc @@ -0,0 +1,453 @@ +/* + * 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/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>( + HF_INET6_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>( + HF_INET6_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>(HF_INET6_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..7d7bcebc3 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rs.cc @@ -0,0 +1,412 @@ +/* + * 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/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>( + HF_INET6_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>( + HF_INET6_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>(HF_INET6_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 index c7e10d111..0973069b1 100644 --- a/libtransport/src/test/test_fec_reedsolomon.cc +++ b/libtransport/src/test/test_fec_reedsolomon.cc @@ -14,6 +14,7 @@ * 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> @@ -30,28 +31,35 @@ 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); - std::vector<fec::buffer> tx_block(k); - std::vector<fec::buffer> rx_block(k); + 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]( - std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p).second); - } - }); + 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( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { + [&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] != *source_packets[i].second) { + if (*tx_block[i].first != *source_packets[i].getBuffer() || + tx_block[i].second != source_packets[i].getMetadata()) { count++; } } @@ -60,7 +68,7 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { do { // Discard eventual packet appended in previous callback call tx_block.erase(tx_block.begin() + k, tx_block.end()); - auto _seq_offet = seq_offset; + uint32_t _seq_offset = seq_offset; // Initialization. Feed encoder with first k source packets for (int i = 0; i < k; i++) { @@ -69,45 +77,46 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { // 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 - (rand() % 100); + 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::generate(packet->writableData(), packet->writableTail(), rand); std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to seq_offset, to reorder at receiver side - packet->writableData()[0] = uint8_t(_seq_offet++); + 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::move(packet); + tx_block[i] = std::make_pair(std::move(packet), metadata); } // Create the repair packets for (auto &tx : tx_block) { - encoder.consume(tx, tx->writableBuffer()[0]); + encoder.consume(tx.first, tx.first->writableBuffer()[0], 0, tx.second); } // Simulate transmission on lossy channel - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); 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(), - std::default_random_engine(seed)); + 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 - decoder.consumeSource(rx_block[rxi - 1], - rx_block[rxi - 1]->data()[0]); + 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]); + decoder.consumeRepair(rx_block[rxi - 1].first); } } @@ -126,6 +135,12 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { 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; @@ -136,33 +151,39 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // Receiver will receive packet for n_sourceblocks in a random order. int total_packets = n * n_sourceblocks; int tx_packets = k * n_sourceblocks; - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p.second), ++i); - } + 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)); + 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])); - } + // 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(); - }); + // Clear tx block for next source block + tx_block.clear(); + encoder.clear(); + }); // The decode callback must be called exactly n_sourceblocks times - decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { - count++; - }); + 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 @@ -173,7 +194,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // 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 - (rand() % 100); + auto cur_size = size - dis(gen); // Set payload, saving 2 bytes at the beginning of the buffer for encoding // the length @@ -182,7 +203,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to i, to reorder at receiver side - packet->writableData()[0] = uint8_t(i); + ((uint32_t *)packet->writableData())[0] = uint32_t(i); // Store packet in tx buffer tx_block.emplace_back(packet, i); @@ -195,8 +216,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { 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(), - std::default_random_engine(seed)); + std::shuffle(rx_block.begin(), rx_block.end(), gen); for (auto &p : rx_block) { int index = p.second % n; @@ -231,7 +251,7 @@ foreach_rs_fec_type #undef _ TEST(ReedSolomonMultiBlockTest, RSMB10) { - int blocks = 10; + int blocks = 1; ReedSolomonMultiBlockTest(blocks); } diff --git a/libtransport/src/test/test_fec_rely_wrapper.cc b/libtransport/src/test/test_fec_rely_wrapper.cc index c5b73f8d2..764e4dd2d 100644 --- a/libtransport/src/test/test_fec_rely_wrapper.cc +++ b/libtransport/src/test/test_fec_rely_wrapper.cc @@ -13,18 +13,23 @@ * 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 { -std::string printMissing( - const std::map<uint32_t, core::ContentObject::Ptr> &missing) { +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) { @@ -48,14 +53,15 @@ std::string printMissing( * @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, + 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 - srand(k * n); + 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; @@ -68,73 +74,78 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, auto &packet_manager = core::PacketManager<>::getInstance(); // Store packets to verify them in the decoder callback - std::map<uint32_t, core::ContentObject::Ptr> saved_packets; + 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) -> fec::buffer { + encoder.setBufferCallback([](std::size_t size) { auto ret = - core::PacketManager<>::getInstance().getPacket<core::ContentObject>(); + core::PacketManager<>::getInstance().getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); ret->updateLength(size); ret->append(size); ret->trimStart(ret->headerSize()); - assert(ret->length() >= size); + DCHECK(ret->length() >= size); return ret; }); // Set callback to be called by encoder when repair packets are ready - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - // We must get n - k symbols - EXPECT_EQ(packets.size(), n - k); - // TRANSPORT_LOGD("Got %zu symbols", packets.size()); - - // Save symbols in pending_repair_packets queue and increment iterations - for (auto &packet : packets) { - ++iterations; - pending_repair_packets.push(packet.second); - } - }); + 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( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - for (auto &packet : packets) { - // TRANSPORT_LOGD("Recovering packet %u", packet.first); + 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; - // Ensure recovered packet is in packets actually produced by encoder - auto original = saved_packets.find(packet.first); - ASSERT_TRUE(original != saved_packets.end()); - auto &original_packet = *original->second; + // 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 */); - // Remove additional headers at the beginning of the packet. This may - // change in the future. - original_packet.trimStart(60 /* Ip + TCP */ + 28 /* Rely header */ + - 4 /* Packet size */); + // Recovered packet should be equal to the original one + EXPECT_TRUE(original_packet == *packet.getBuffer()); - // Recovered packet should be equal to the original one - EXPECT_TRUE(original_packet == *packet.second); + // Also metadata should correspond + EXPECT_TRUE(original->second.second == packet.getMetadata()); - // Restore removed headers - original_packet.prepend(60 + 28 + 4); + // Restore removed headers + original_packet.prepend(60 + 32 + 4); - // Erase packet from saved packet list - saved_packets.erase(original); - } - }); + // 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 + (rand() % 1300); + auto payload_size = 50 + (dis(gen)); uint8_t payload[max_packet_size]; - std::generate(payload, payload + payload_size, rand); + std::generate(payload, payload + payload_size, gen); // Get a packet from global pool and set name - auto buffer = packet_manager.getPacket<core::ContentObject>(); + auto buffer = packet_manager.getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); buffer->setName(core::Name("b001::abcd", iterations)); // Get offset @@ -145,20 +156,23 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // 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 - // TRANSPORT_LOGD("Saving packet with index %lu", iterations); - saved_packets.emplace(iterations, buffer); + 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); + 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; - // TRANSPORT_LOGD("Difference is %zu", diff); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Difference is " << diff; auto cmp = std::memcmp(buffer->data() + offset + diff, payload, payload_size); EXPECT_FALSE(cmp); @@ -170,29 +184,33 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // 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) \ - (rand() % 100) >= loss_rate || iterations >= max_iterations * 0.9 + (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 - // TRANSPORT_LOGD("Passing packet %u to decoder", - // buffer->getName().getSuffix()); - decoder.onDataPacket(*buffer, offset); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << buffer->getName().getSuffix() + << " to decoder"; + decoder.onDataPacket(*buffer, offset, metadata); } else { - // TRANSPORT_LOGD("Packet %u, dropped", buffer->getName().getSuffix()); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << buffer->getName().getSuffix() << " dropped"; } - // Check if previous call to encoder.consumer() generated repair packets, + // 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(); - // TRANSPORT_LOGD("Passing packet %u to decoder", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << iterations << " to decoder"; core::ContentObject &co = (core::ContentObject &)(*packet); decoder.onDataPacket(co, 0); } else { - // TRANSPORT_LOGD("Packet (repair) %u dropped", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet (repair) " << iterations << " dropped"; } // Remove packet from the queue @@ -206,9 +224,6 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // 0.001 residual losses EXPECT_LE(saved_packets.size(), iterations * 0.001) << printMissing(saved_packets); - - // Reset seed - srand(time(0)); } /** 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_interest.cc b/libtransport/src/test/test_interest.cc index 8853563b0..d9c535881 100644 --- a/libtransport/src/test/test_interest.cc +++ b/libtransport/src/test/test_interest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * 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: @@ -30,7 +30,7 @@ namespace { // The fixture for testing class Foo. class InterestTest : public ::testing::Test { protected: - InterestTest() : name_("b001::123|321"), interest_() { + InterestTest() : name_("b001::123|321"), interest_(HF_INET6_TCP) { // You can do set-up work for each test here. } @@ -108,7 +108,7 @@ TEST_F(InterestTest, ConstructorWithName) { Name n("b001::1|123"); try { - Interest interest(n); + Interest interest(HF_INET6_TCP, n); } catch (...) { FAIL() << "ERROR: Unexpected exception thrown"; } @@ -176,27 +176,27 @@ TEST_F(InterestTest, SetGetLocator) { auto l = interest.getLocator(); ip_address_t address; - ip_address_pton("b006::ab:cdab:cdef", &address); - auto ret = !std::memcmp(&l, &address, sizeof(address)); + inet_pton(AF_INET6, "b006::ab:cdab:cdef", &address); + auto ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); // Set different locator - ip_address_pton("2001::1234::4321::abcd::", &address); + inet_pton(AF_INET6, "2001::1234::4321::abcd::", &address); // Set it on interest interest.setLocator(address); // Check it was set l = interest.getLocator(); - ret = !std::memcmp(&l, &address, sizeof(address)); + ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); } TEST_F(InterestTest, SetGetLifetime) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); const constexpr uint32_t lifetime = 10000; // Set lifetime @@ -211,7 +211,7 @@ TEST_F(InterestTest, SetGetLifetime) { TEST_F(InterestTest, HasManifest) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Let's expect anexception here try { @@ -232,7 +232,7 @@ TEST_F(InterestTest, HasManifest) { TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Appenad some suffixes, with some duplicates interest.appendSuffix(1); diff --git a/libtransport/src/test/test_memif_connector.cc b/libtransport/src/test/test_memif_connector.cc new file mode 100644 index 000000000..562a12c88 --- /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::getDurationS(t0_, t1); + auto rate = recv_counter_ / 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 index 76ad352d6..ca20cdfb7 100644 --- a/libtransport/src/test/test_packet.cc +++ b/libtransport/src/test/test_packet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * 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: @@ -16,6 +16,7 @@ #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> @@ -33,7 +34,7 @@ namespace core { class PacketForTest : public Packet { public: template <typename... Args> - PacketForTest(Args &&... args) : Packet(std::forward<Args>(args)...) {} + PacketForTest(Args &&...args) : Packet(std::forward<Args>(args)...) {} virtual ~PacketForTest() {} @@ -302,7 +303,7 @@ TEST_F(PacketTest, ConstructorWithNew) { auto &_packet = raw_packets_[HF_INET6_TCP]; auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size()); - (void)packet_ptr; + delete packet_ptr; } TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) { @@ -682,9 +683,7 @@ 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 = - duration_cast<milliseconds>(system_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); try { packet.setSignatureTimestamp(now); diff --git a/libtransport/src/test/test_packet_allocator.cc b/libtransport/src/test/test_packet_allocator.cc new file mode 100644 index 000000000..b63ddde8d --- /dev/null +++ b/libtransport/src/test/test_packet_allocator.cc @@ -0,0 +1,106 @@ +/* + * 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/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/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; + + // 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>(HF_INET_TCP); +} + +TEST_F(PacketAllocatorTest, InterestAllocation) { + allocationTest<core::Interest>(HF_INET_TCP); +} + +// TEST_F(PacketAllocatorTest, MemBufAllocation) { +// allocationTest<::utils::MemBuf>(); +// } + +TEST_F(PacketAllocatorTest, CheckAllocationIsCorrect) { + // Create packet + auto packet = allocator_.getPacket<core::ContentObject>(HF_INET_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))); +} + +} // namespace core +} // namespace transport
\ 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 |