diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index f9396cf3ac..9186e3356a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -215,7 +215,7 @@ jobs: - name: Test PcapPlusPlus run: | . .venv/bin/activate - python3 ci/run_tests/run_tests.py --interface eth0 --test-suites "packet" + python3 ci/run_tests/run_tests.py --interface eth0 --test-suites "common" "packet" - name: Check installation run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 44ef689f39..85870c95f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: '.*\.(pcap|pcapng|dat)|(PacketExamples|PcapExamples|expected_output|pcap_examples).*\.txt' +exclude: '.*\.(pcap|pcapng|dat)|(PacketExamples|PcapExamples|expected_output|pcap_examples).*\.txt|3rdParty/googletest-1.16.0/googletest/' fail_fast: false repos: - repo: local @@ -36,7 +36,7 @@ repos: args: ["--style=file", "-i"] # Use the .clang-format file for configuration and apply all fixes files: ^(Common\+\+|Packet\+\+|Pcap\+\+|Tests|Examples)/.*\.(cpp|h)$ - id: cppcheck - args: ["--std=c++14", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force"] + args: ["--std=c++14", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force", "--library=googletest"] - repo: https://github.com/BlankSpruce/gersemi rev: 0.22.1 hooks: diff --git a/Common++/header/IpAddress.h b/Common++/header/IpAddress.h index f9282a9151..7eb8484078 100644 --- a/Common++/header/IpAddress.h +++ b/Common++/header/IpAddress.h @@ -883,6 +883,19 @@ namespace pcpp std::unique_ptr m_IPv6Network; }; + namespace literals + { + inline IPv4Address operator""_ipv4(const char* addrString, std::size_t size) + { + return IPv4Address(std::string(addrString, size)); + } + + inline IPv6Address operator""_ipv6(const char* addrString, std::size_t size) + { + return IPv6Address(std::string(addrString, size)); + } + } // namespace literals + inline std::ostream& operator<<(std::ostream& oss, const pcpp::IPv4Address& ipv4Address) { oss << ipv4Address.toString(); diff --git a/Common++/header/MacAddress.h b/Common++/header/MacAddress.h index bcf6e28b83..6493aee20d 100644 --- a/Common++/header/MacAddress.h +++ b/Common++/header/MacAddress.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -177,9 +178,9 @@ namespace pcpp bool copyToNewBuffer(uint8_t** buffer, size_t& size) const; /// A static value representing a zero value of MAC address, meaning address of value "00:00:00:00:00:00" - static MacAddress Zero; + static const MacAddress Zero; /// A static value representing a broadcast MAC address, meaning address of value "ff:ff:ff:ff:ff:ff" - static MacAddress Broadcast; + static const MacAddress Broadcast; private: std::array m_Address{}; diff --git a/Common++/src/MacAddress.cpp b/Common++/src/MacAddress.cpp index a65591bbf4..4f32599a43 100644 --- a/Common++/src/MacAddress.cpp +++ b/Common++/src/MacAddress.cpp @@ -3,9 +3,9 @@ namespace pcpp { - MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); + const MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); - MacAddress MacAddress::Broadcast(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + const MacAddress MacAddress::Broadcast(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); std::string MacAddress::toString() const { diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f684a16972..6b008a1ec8 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,6 +1,7 @@ if(PCAPPP_BUILD_TESTS) add_subdirectory(PcppTestFramework) add_subdirectory(PcppTestUtilities) + add_subdirectory(Common++Test) add_subdirectory(Packet++Test) if(PCAPPP_BUILD_PCAPPP) diff --git a/Tests/Common++Test/CMakeLists.txt b/Tests/Common++Test/CMakeLists.txt new file mode 100644 index 0000000000..449649fe2c --- /dev/null +++ b/Tests/Common++Test/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.14) + +include(FetchContent) + +# TODO: This may have issues with brew as it doesn't allow the use of FetchContent. +FetchContent_Declare(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.16.0) + +if(WIN32) + # Prevent overriding the parent project's compiler/linker settings. + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +endif() + +FetchContent_MakeAvailable(googletest) + +add_executable( + Common++Test + "main.cpp" + "Tests/GeneralUtilsTests.cpp" + "Tests/IPAddressTests.cpp" + "Tests/LoggerTests.cpp" + "Tests/LRUListTests.cpp" + "Tests/MacAddressTests.cpp" + "Tests/ObjectPoolTests.cpp" + "Tests/PointerVectorTests.cpp" + "Tests/SystemUtilsTests.cpp" + "Tests/TimespecTimevalTests.cpp" + "Utils/MemoryLeakListener.cpp" +) + +target_link_libraries(Common++Test PRIVATE Common++ memplumber gtest gmock) + +target_include_directories(Common++Test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_precompile_headers(Common++Test PRIVATE "pch.h") + +if(MSVC) + # This executable requires getopt.h not available on VStudio + target_link_libraries(Common++Test PRIVATE Getopt-for-Visual-Studio) +endif() + +set_property(TARGET Common++Test PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Bin") +set_property(TARGET Common++Test PROPERTY RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/Bin") +set_property(TARGET Common++Test PROPERTY RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/Bin") + +enable_testing() + +add_test(NAME Common++Test COMMAND $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/) diff --git a/Tests/Common++Test/Tests/GeneralUtilsTests.cpp b/Tests/Common++Test/Tests/GeneralUtilsTests.cpp new file mode 100644 index 0000000000..1a3ba4c3c2 --- /dev/null +++ b/Tests/Common++Test/Tests/GeneralUtilsTests.cpp @@ -0,0 +1,37 @@ +#include "pch.h" + +#include + +#include "GeneralUtils.h" + +namespace pcpp +{ + TEST(GeneralUtilsTests, byteArrayToHexString) + { + std::array byteArr = { 0xaa, 0x2b, 0x10 }; + EXPECT_EQ(byteArrayToHexString(byteArr.data(), byteArr.size()), "aa2b10"); + }; + + TEST(GeneralUtilsTests, hexStringToByteArray) + { + std::array resultByteArr = { 0 }; + EXPECT_EQ(hexStringToByteArray("aa2b10", resultByteArr.data(), resultByteArr.size()), 3); + EXPECT_EQ(resultByteArr, (std::array{ 0xaa, 0x2b, 0x10 })); + }; + + TEST(GeneralUtilsTests, cross_platform_memmem) + { + const char haystack[] = "Hello, World!"; + const char needle[] = "World"; + EXPECT_EQ(cross_platform_memmem(haystack, sizeof(haystack), needle, + sizeof(needle) - 1 /* ignore the null terminator */), + haystack + 7); + }; + + TEST(GeneralUtilsTests, align) + { + EXPECT_EQ(align<4>(3), 4); + EXPECT_EQ(align<4>(4), 4); + EXPECT_EQ(align<4>(5), 8); + }; +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/IPAddressTests.cpp b/Tests/Common++Test/Tests/IPAddressTests.cpp new file mode 100644 index 0000000000..37968b7686 --- /dev/null +++ b/Tests/Common++Test/Tests/IPAddressTests.cpp @@ -0,0 +1,809 @@ +#include "pch.h" + +#include +#include + +#include "IpAddress.h" + +namespace pcpp +{ + TEST(IPv4AddressTest, DefaultConstructor) + { + IPv4Address addr1; + EXPECT_EQ(addr1.toString(), "0.0.0.0"); + } + + TEST(IPv4AddressTest, ConstructorWithInteger) + { + IPv4Address addr2(0x0100A8C0); // 192.168.0.1 + EXPECT_EQ(addr2.toString(), "192.168.0.1"); + } + + TEST(IPv4AddressTest, ConstructorWithByteArray) + { + uint8_t bytes[4] = { 192, 168, 0, 1 }; + IPv4Address addr3(bytes); + EXPECT_EQ(addr3.toString(), "192.168.0.1"); + } + + TEST(IPv4AddressTest, ConstructorWithStdArray) + { + std::array byteArray = { 192, 168, 0, 1 }; + IPv4Address addr4(byteArray); + EXPECT_EQ(addr4.toString(), "192.168.0.1"); + } + + TEST(IPv4AddressTest, ConstructorWithString) + { + IPv4Address addr5("192.168.0.1"); + EXPECT_EQ(addr5.toString(), "192.168.0.1"); + } + + TEST(IPv4AddressTest, ToBytesMethod) + { + std::array bytes = { 192, 168, 0, 1 }; + IPv4Address addr5("192.168.0.1"); + const uint8_t* addrBytes = addr5.toBytes(); + EXPECT_EQ(memcmp(addrBytes, bytes.data(), 4), 0); + } + + TEST(IPv4AddressTest, IsMulticastMethod) + { + IPv4Address underMulticastBound(0x000000D1); + EXPECT_FALSE(underMulticastBound.isMulticast()); + + IPv4Address atLowerMulticastBound(0x000000E0); + EXPECT_TRUE(atLowerMulticastBound.isMulticast()); + + IPv4Address inMulticastRange(0x000000EF); + EXPECT_TRUE(inMulticastRange.isMulticast()); + + IPv4Address atUpperMulticastBound(0xFFFFFFEF); + EXPECT_TRUE(atUpperMulticastBound.isMulticast()); + + IPv4Address overMulticastBound(0x000000F0); + EXPECT_FALSE(overMulticastBound.isMulticast()); + } + + TEST(IPv4AddressTest, EqualityOperator) + { + IPv4Address addr5("192.168.0.1"); + IPv4Address addr6("192.168.0.1"); + EXPECT_TRUE(addr5 == addr6); + IPv4Address addr7("192.168.0.2"); + EXPECT_FALSE(addr5 == addr7); + } + + TEST(IPv4AddressTest, LessThanOperator) + { + IPv4Address addr5("192.168.0.1"); + IPv4Address addr7("192.168.0.2"); + EXPECT_TRUE(addr5 < addr7); + EXPECT_FALSE(addr7 < addr5); + } + + TEST(IPv4AddressTest, MatchNetworkMethodWithIPv4Network) + { + IPv4Address addr5("192.168.0.1"); + IPv4Network network("192.168.0.0/24"); + EXPECT_TRUE(addr5.matchNetwork(network)); + + IPv4Network network2("192.168.1.0/24"); + EXPECT_FALSE(addr5.matchNetwork(network2)); + + IPv4Network network3("192.168.1.0/16"); + EXPECT_TRUE(addr5.matchNetwork(network3)); + } + + TEST(IPv4AddressTest, MatchNetworkMethodWithString) + { + IPv4Address addr5("192.168.0.1"); + EXPECT_TRUE(addr5.matchNetwork("192.168.0.0/24")); + EXPECT_FALSE(addr5.matchNetwork("192.168.1.0/24")); + EXPECT_TRUE(addr5.matchNetwork("192.168.1.0/16")); + } + + TEST(IPv4AddressTest, IsValidIPv4AddressStaticMethod) + { + EXPECT_TRUE(IPv4Address::isValidIPv4Address("192.168.0.1")); + EXPECT_FALSE(IPv4Address::isValidIPv4Address("999.999.999.999")); + EXPECT_FALSE(IPv4Address::isValidIPv4Address("bogus string")); + } + + TEST(IPv4AddressTest, OutputStreamOperator) + { + IPAddress ip("192.100.1.1"); + std::stringstream ss; + ss << ip; + EXPECT_EQ(ss.str(), "192.100.1.1"); + } + + TEST(IPv4AddressTest, ConstantHelpers) + { + EXPECT_EQ(IPv4Address::Zero.toString(), "0.0.0.0"); + EXPECT_EQ(IPv4Address::MulticastRangeLowerBound.toString(), "224.0.0.0"); + EXPECT_EQ(IPv4Address::MulticastRangeUpperBound.toString(), "239.255.255.255"); + }; + + TEST(IPv4AddressTest, Literals) + { + using namespace pcpp::literals; + + IPv4Address ipString = "192.168.1.5"_ipv4; + EXPECT_EQ(ipString.toInt(), 0x0501A8C0); + } + + TEST(IPv6AddressTest, DefaultConstructor) + { + IPv6Address addr1; + EXPECT_EQ(addr1.toString(), "::"); + } + + TEST(IPv6AddressTest, ConstructorWithByteArray) + { + uint8_t bytes[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }; + IPv6Address addr2(bytes); + EXPECT_EQ(addr2.toString(), "2001:db8:85a3::8a2e:370:7334"); + } + + TEST(IPv6AddressTest, ConstructorWithStdArray) + { + std::array byteArray = { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }; + IPv6Address addr3(byteArray); + EXPECT_EQ(addr3.toString(), "2001:db8:85a3::8a2e:370:7334"); + } + + TEST(IPv6AddressTest, ConstructorWithString) + { + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + EXPECT_EQ(addr4.toString(), "2001:db8:85a3::8a2e:370:7334"); + + EXPECT_THROW(IPv6Address("2001:0db8:85a3:0000:0000:8a4e:0370:7334:extra"), std::invalid_argument) + << "IPv6Address does not throw for out of bounds IP string."; + EXPECT_THROW(IPv6Address("2001::ab01::c"), std::invalid_argument) + << "IPv6Address does not throw for multiple double colon in IP string."; + EXPECT_THROW(IPv6Address("bogusString"), std::invalid_argument) + << "IPv6Address does not throw for non-IP string."; + } + + TEST(IPv6AddressTest, ToBytesMethod) + { + std::array bytes = { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }; + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + const uint8_t* addrBytes = addr4.toBytes(); + EXPECT_EQ(memcmp(addrBytes, bytes.data(), 16), 0); + } + + TEST(IPv6AddressTest, IsMulticastMethod) + { + IPv6Address underMulticastBound("fef0::"); + EXPECT_FALSE(underMulticastBound.isMulticast()); + + IPv6Address atLowerMulticastBound("ff00::"); + EXPECT_TRUE(atLowerMulticastBound.isMulticast()); + + IPv6Address inMulticastRange("ff00::ef"); + EXPECT_TRUE(inMulticastRange.isMulticast()); + } + + TEST(IPv6AddressTest, EqualityOperator) + { + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + IPv6Address addr5("2001:db8:85a3::8a2e:370:7334"); + EXPECT_TRUE(addr4 == addr5); + IPv6Address addr6("2001:db8:85a3::8a2e:370:7335"); + EXPECT_FALSE(addr4 == addr6); + } + + TEST(IPv6AddressTest, LessThanOperator) + { + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + IPv6Address addr6("2001:db8:85a3::8a2e:370:7335"); + EXPECT_TRUE(addr4 < addr6); + EXPECT_FALSE(addr6 < addr4); + } + + TEST(IPv6AddressTest, CopyToBuffer) + { + IPv6Address addr("2001:db8:85a3::8a2e:370:7334"); + std::array expected = { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }; + + // Test query mode + EXPECT_EQ(addr.copyTo(nullptr, 0), 16); + + // Test with null buffer and non-zero size + EXPECT_THROW(addr.copyTo(nullptr, 1), std::invalid_argument); + + std::array buffer{}; + + // Test with smaller buffer. + EXPECT_EQ(addr.copyTo(buffer.data(), 5), 16); + EXPECT_THAT(buffer, ::testing::Each(::testing::Eq(0))); + + // Test with precise buffer + buffer.fill(0); + EXPECT_EQ(addr.copyTo(buffer.data(), 16), 16); + EXPECT_EQ(std::memcmp(buffer.data(), expected.data(), 16), 0); + EXPECT_TRUE(std::all_of(buffer.begin() + 16, buffer.end(), [](uint8_t x) { return x == 0; })); + + // Test with a buffer that is larger + buffer.fill(0); + EXPECT_EQ(addr.copyTo(buffer.data(), buffer.size()), 16); + EXPECT_EQ(std::memcmp(buffer.data(), expected.data(), 16), 0); + EXPECT_TRUE(std::all_of(buffer.begin() + 16, buffer.end(), [](uint8_t x) { return x == 0; })); + } + + TEST(IPv6AddressTest, CopyToBufferNewBuffer) + { + IPv6Address addr("2001:db8:85a3::8a2e:370:7334"); + std::array expected = { 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }; + + uint8_t* newBuffer = nullptr; + size_t newBufferSize = 0; + + EXPECT_THROW(addr.copyToNewBuffer(nullptr, newBufferSize), std::invalid_argument) + << "IPv6Address::copyToNewBuffer does not throw for null buffer pointer."; + + EXPECT_TRUE(addr.copyToNewBuffer(&newBuffer, newBufferSize)); + std::unique_ptr bufferGuard(newBuffer); + + ASSERT_NE(newBuffer, nullptr) << "IPv6Address::copyToNewBuffer did not allocate a new buffer."; + ASSERT_EQ(newBufferSize, 16) << "IPv6Address::copyToNewBuffer did not return the correct size."; + + EXPECT_EQ(std::memcmp(newBuffer, expected.data(), 16), 0) + << "IPv6Address::copyToNewBuffer did not copy the address correctly."; + } + + TEST(IPv6AddressTest, MatchNetworkMethodWithIPv6Network) + { + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + IPv6Network network("2001:db8::/32"); + EXPECT_TRUE(addr4.matchNetwork(network)); + + IPv6Network network2("2001:db9::/32"); + EXPECT_FALSE(addr4.matchNetwork(network2)); + } + + TEST(IPv6AddressTest, MatchNetworkMethodWithString) + { + IPv6Address addr4("2001:db8:85a3::8a2e:370:7334"); + EXPECT_TRUE(addr4.matchNetwork("2001:db8::/32")); + EXPECT_FALSE(addr4.matchNetwork("2001:db9::/32")); + } + + TEST(IPv6AddressTest, ConstantHelpers) + { + EXPECT_THAT(IPv6Address::Zero.toByteArray(), ::testing::Each(0)); + + EXPECT_THAT(IPv6Address::MulticastRangeLowerBound.toByteArray(), + ::testing::ElementsAre(0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00)); + }; + + TEST(IPv6AddressTest, OutputStreamOperator) + { + IPv6Address ip("2001:db8:85a3::8a2e:370:7334"); + std::stringstream ss; + ss << ip; + EXPECT_EQ(ss.str(), "2001:db8:85a3::8a2e:370:7334"); + } + + TEST(IPv6AddressTest, Literals) + { + using namespace pcpp::literals; + + IPv6Address ipString = "2001:0db8:85a3:0000:0000:8a4e:0370:7334"_ipv6; + EXPECT_THAT(ipString.toByteArray(), ::testing::ElementsAre(0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, + 0x00, 0x8A, 0x4E, 0x03, 0x70, 0x73, 0x34)); + } + + TEST(IPAddressTest, DefaultConstructor) + { + IPAddress ipDefault; + EXPECT_EQ(ipDefault.getType(), IPAddress::AddressType::IPv4AddressType); + EXPECT_EQ(ipDefault.getIPv4(), IPv4Address::Zero); + } + + TEST(IPAddressTest, ConstructorWithIPv4Address) + { + IPv4Address ipv4Addr("192.168.0.1"); + IPAddress addr1(ipv4Addr); + EXPECT_EQ(addr1.getType(), IPAddress::AddressType::IPv4AddressType); + EXPECT_EQ(addr1.getIPv4(), ipv4Addr); + } + + TEST(IPAddressTest, ConstructorWithIPv6Address) + { + IPv6Address ipv6Addr("2001:db8:85a3::8a2e:370:7334"); + IPAddress addr2(ipv6Addr); + EXPECT_EQ(addr2.getType(), IPAddress::AddressType::IPv6AddressType); + EXPECT_EQ(addr2.getIPv6(), ipv6Addr); + } + + TEST(IPAddressTest, ConstructorWithString) + { + IPAddress ipv4String("192.168.0.1"); + EXPECT_EQ(ipv4String.getType(), IPAddress::AddressType::IPv4AddressType); + EXPECT_EQ(ipv4String.getIPv4(), IPv4Address("192.168.0.1")); + + EXPECT_THROW(IPAddress("192.168.300.1"), std::invalid_argument); + + IPAddress ip6String("2001:db8:85a3::8a2e:370:7334"); + EXPECT_EQ(ip6String.getType(), IPAddress::AddressType::IPv6AddressType); + EXPECT_EQ(ip6String.getIPv6(), IPv6Address("2001:db8:85a3::8a2e:370:7334")); + + EXPECT_THROW(IPAddress("2001:db8:85a3::8a2e:370:7334:extra"), std::invalid_argument); + EXPECT_THROW(IPv6Address("2001::ab01::c"), std::invalid_argument); + EXPECT_THROW(IPAddress("bogusString"), std::invalid_argument); + } + + TEST(IPAddressTest, AssignmentOperatorWithIPv4Address) + { + IPv4Address ipv4Addr("192.168.0.1"); + IPAddress ipAddr; + ASSERT_EQ(ipAddr.getType(), IPAddress::AddressType::IPv4AddressType); + ASSERT_EQ(ipAddr.getIPv4(), IPv4Address::Zero); + + ipAddr = ipv4Addr; + + EXPECT_EQ(ipAddr.getType(), IPAddress::AddressType::IPv4AddressType); + EXPECT_EQ(ipAddr.getIPv4(), ipv4Addr); + } + + TEST(IPAddressTest, AssignmentOperatorWithIPv6Address) + { + IPv6Address ipv6Addr("2001:db8:85a3::8a2e:370:7334"); + IPAddress ipAddr; + ASSERT_EQ(ipAddr.getType(), IPAddress::AddressType::IPv4AddressType); + ASSERT_EQ(ipAddr.getIPv4(), IPv4Address::Zero); + + ipAddr = ipv6Addr; + EXPECT_EQ(ipAddr.getType(), IPAddress::AddressType::IPv6AddressType); + EXPECT_EQ(ipAddr.getIPv6(), ipv6Addr); + } + + TEST(IPAddressTest, IsIPv4Method) + { + IPAddress ip4("192.168.0.1"); + EXPECT_TRUE(ip4.isIPv4()); + + IPAddress ip6("2001:db8:85a3::8a2e:370:7334"); + EXPECT_FALSE(ip6.isIPv4()); + } + + TEST(IPAddressTest, IsIPv6Method) + { + IPAddress ip4("192.168.0.1"); + EXPECT_FALSE(ip4.isIPv6()); + + IPAddress ip6("2001:db8:85a3::8a2e:370:7334"); + EXPECT_TRUE(ip6.isIPv6()); + } + + TEST(IPAddressTest, IsMulticastMethod) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("IPv4"); + + IPAddress underMulticastBound("223.0.0.0"_ipv4); + EXPECT_FALSE(underMulticastBound.isMulticast()); + + IPAddress atLowerMulticastBound("224.0.0.0"_ipv4); + EXPECT_TRUE(atLowerMulticastBound.isMulticast()); + + IPAddress inMulticastRange("230.9.4.1"_ipv4); + EXPECT_TRUE(inMulticastRange.isMulticast()); + + IPAddress atUpperMulticastBound("239.255.255.255"_ipv4); + EXPECT_TRUE(atUpperMulticastBound.isMulticast()); + + IPAddress overMulticastBound("240.0.0.0"_ipv4); + EXPECT_FALSE(overMulticastBound.isMulticast()); + } + + { + SCOPED_TRACE("IPv6"); + + IPAddress underMulticastBound("fef0::"_ipv6); + EXPECT_FALSE(underMulticastBound.isMulticast()); + + IPAddress atLowerMulticastBound("ff00::"_ipv6); + EXPECT_TRUE(atLowerMulticastBound.isMulticast()); + + IPAddress inMulticastRange("ff00::ef"_ipv6); + EXPECT_TRUE(inMulticastRange.isMulticast()); + } + }; + + TEST(IPAddressTest, OutputStreamOperrator) + { + IPAddress ip4("192.168.0.1"); + std::stringstream ss; + ss << ip4; + EXPECT_EQ(ss.str(), "192.168.0.1"); + + IPAddress ip6("2001:db8:85a3::8a2e:370:7334"); + ss.str(""); + ss << ip6; + EXPECT_EQ(ss.str(), "2001:db8:85a3::8a2e:370:7334"); + } + + TEST(IPv4NetworkTest, ConstructorWithSingleAddress) + { + using namespace pcpp::literals; + + IPv4Network netSingle("192.168.1.1"_ipv4); + EXPECT_EQ(netSingle.getPrefixLen(), 32u); + EXPECT_EQ(netSingle.getNetmask(), "255.255.255.255"); + EXPECT_EQ(netSingle.getNetworkPrefix(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingle.getLowestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingle.getHighestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingle.getTotalAddressCount(), 1); + EXPECT_EQ(netSingle.toString(), "192.168.1.1/32"); + } + + TEST(IPv4NetworkTest, ConstructorWithAddressAndPrefix) + { + using namespace pcpp::literals; + + IPv4Network netPrefix("192.168.1.1"_ipv4, 24u); + EXPECT_EQ(netPrefix.getPrefixLen(), 24u); + EXPECT_EQ(netPrefix.getNetmask(), "255.255.255.0"); + EXPECT_EQ(netPrefix.getNetworkPrefix(), "192.168.1.0"_ipv4); + EXPECT_EQ(netPrefix.getLowestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netPrefix.getHighestAddress(), "192.168.1.254"_ipv4); + EXPECT_EQ(netPrefix.getTotalAddressCount(), 256); + EXPECT_EQ(netPrefix.toString(), "192.168.1.0/24"); + } + + TEST(IPv4NetworkTest, ConstructorWithAddressAndNetmask) + { + using namespace pcpp::literals; + + IPv4Network netNetmask("192.168.1.1"_ipv4, "255.255.0.0"); + EXPECT_EQ(netNetmask.getPrefixLen(), 16u); + EXPECT_EQ(netNetmask.getNetmask(), "255.255.0.0"); + EXPECT_EQ(netNetmask.getNetworkPrefix(), "192.168.0.0"_ipv4); + EXPECT_EQ(netNetmask.getLowestAddress(), "192.168.0.1"_ipv4); + EXPECT_EQ(netNetmask.getHighestAddress(), "192.168.255.254"_ipv4); + EXPECT_EQ(netNetmask.getTotalAddressCount(), 256 * 256); + EXPECT_EQ(netNetmask.toString(), "192.168.0.0/16"); + } + + TEST(IPv4NetworkTest, ConstructorWithString) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("Valid c'tor: IPv4 address + prefix len"); + + IPv4Network netStringWithPrefix("192.168.1.1/8"); + EXPECT_EQ(netStringWithPrefix.getPrefixLen(), 8u); + EXPECT_EQ(netStringWithPrefix.getNetmask(), "255.0.0.0"); + EXPECT_EQ(netStringWithPrefix.getNetworkPrefix(), "192.0.0.0"_ipv4); + EXPECT_EQ(netStringWithPrefix.getLowestAddress(), "192.0.0.1"_ipv4); + EXPECT_EQ(netStringWithPrefix.getHighestAddress(), "192.255.255.254"_ipv4); + EXPECT_EQ(netStringWithPrefix.getTotalAddressCount(), 256 * 256 * 256); + EXPECT_EQ(netStringWithPrefix.toString(), "192.0.0.0/8"); + } + + { + SCOPED_TRACE("Valid c'tor: IPv4 address + netmask"); + + IPv4Network netStringWithMask("192.168.1.1/255.0.0.0"); + EXPECT_EQ(netStringWithMask.getPrefixLen(), 8u); + EXPECT_EQ(netStringWithMask.getNetmask(), "255.0.0.0"); + EXPECT_EQ(netStringWithMask.getNetworkPrefix(), "192.0.0.0"_ipv4); + EXPECT_EQ(netStringWithMask.getLowestAddress(), "192.0.0.1"_ipv4); + EXPECT_EQ(netStringWithMask.getHighestAddress(), "192.255.255.254"_ipv4); + EXPECT_EQ(netStringWithMask.getTotalAddressCount(), 256 * 256 * 256); + EXPECT_EQ(netStringWithMask.toString(), "192.0.0.0/8"); + } + } + + TEST(IPv4NetworkTest, IncludesMethod) + { + using namespace pcpp::literals; + + IPv4Network netBase("192.168.0.0/16"); + + { + SCOPED_TRACE("With single address"); + + EXPECT_TRUE(netBase.includes("192.168.1.0"_ipv4)); + EXPECT_TRUE(netBase.includes("192.168.1.1"_ipv4)); + EXPECT_TRUE(netBase.includes("192.168.2.1"_ipv4)); + EXPECT_FALSE(netBase.includes("192.169.2.1"_ipv4)); + } + + { + SCOPED_TRACE("With network"); + + EXPECT_TRUE(netBase.includes(IPv4Network("192.168.1.0/24"))); + EXPECT_TRUE(netBase.includes(IPv4Network("192.168.2.0/24"))); + EXPECT_TRUE(netBase.includes(IPv4Network("192.168.0.0/16"))); + EXPECT_FALSE(netBase.includes(IPv4Network("192.0.0.0/8"))); + } + }; + + TEST(IPv4NetworkTest, OutputStreamOperator) + { + IPv4Network net("192.168.1.1/32"); + std::stringstream ss; + ss << net; + EXPECT_EQ(ss.str(), "192.168.1.1/32"); + } + + TEST(IPv6NetworkTest, ConstructorWithSingleAddress) + { + using namespace pcpp::literals; + + IPv6Network netSingle("2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingle.getPrefixLen(), 128u); + EXPECT_EQ(netSingle.getNetmask(), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + EXPECT_EQ(netSingle.getNetworkPrefix(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingle.getLowestAddress(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingle.getHighestAddress(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingle.getTotalAddressCount(), 1); + EXPECT_EQ(netSingle.toString(), "2001:db8:85a3::8a2e:370:7334/128"); + } + + TEST(IPv6NetworkTest, ConstructorWithAddressAndPrefix) + { + using namespace pcpp::literals; + + IPv6Network netPrefix("2001:db8:85a3::8a2e:370:7334"_ipv6, 96u); + EXPECT_EQ(netPrefix.getPrefixLen(), 96u); + EXPECT_EQ(netPrefix.getNetmask(), "ffff:ffff:ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netPrefix.getNetworkPrefix(), "2001:db8:85a3::8a2e:0:0"_ipv6); + EXPECT_EQ(netPrefix.getLowestAddress(), "2001:db8:85a3::8a2e:0:1"_ipv6); + EXPECT_EQ(netPrefix.getHighestAddress(), "2001:db8:85a3::8a2e:ffff:ffff"_ipv6); + EXPECT_EQ(netPrefix.getTotalAddressCount(), 4294967296ul); + EXPECT_EQ(netPrefix.toString(), "2001:db8:85a3::8a2e:0:0/96"); + } + + TEST(IPv6NetworkTest, ConstructorWithAddressAndNetmask) + { + using namespace pcpp::literals; + + IPv6Network netNetmask("2001:db8:85a3::8a2e:370:7334"_ipv6, "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netNetmask.getPrefixLen(), 64u); + EXPECT_EQ(netNetmask.getNetmask(), "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netNetmask.getNetworkPrefix(), "2001:db8:85a3::"_ipv6); + EXPECT_EQ(netNetmask.getLowestAddress(), "2001:db8:85a3::1"_ipv6); + EXPECT_EQ(netNetmask.getHighestAddress(), "2001:db8:85a3::ffff:ffff:ffff:ffff"_ipv6); + EXPECT_THROW(netNetmask.getTotalAddressCount(), std::out_of_range); + EXPECT_EQ(netNetmask.toString(), "2001:db8:85a3::/64"); + } + + TEST(IPv6NetworkTest, ConstructorWithString) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("Valid c'tor: IPv6 address + prefix len"); + + IPv6Network netStringWithPrefix("2001:db8:85a3::8a2e:370:7334/64"); + EXPECT_EQ(netStringWithPrefix.getPrefixLen(), 64u); + EXPECT_EQ(netStringWithPrefix.getNetmask(), "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netStringWithPrefix.getNetworkPrefix(), "2001:db8:85a3::"_ipv6); + EXPECT_EQ(netStringWithPrefix.getLowestAddress(), "2001:db8:85a3::1"_ipv6); + EXPECT_EQ(netStringWithPrefix.getHighestAddress(), "2001:db8:85a3::ffff:ffff:ffff:ffff"_ipv6); + EXPECT_THROW(netStringWithPrefix.getTotalAddressCount(), std::out_of_range); + EXPECT_EQ(netStringWithPrefix.toString(), "2001:db8:85a3::/64"); + } + + { + SCOPED_TRACE("Valid c'tor: IPv6 address + netmask"); + + IPv6Network netStringWithMask("2001:db8:85a3::8a2e:370:7334/ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netStringWithMask.getPrefixLen(), 64u); + EXPECT_EQ(netStringWithMask.getNetmask(), "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netStringWithMask.getNetworkPrefix(), "2001:db8:85a3::"_ipv6); + EXPECT_EQ(netStringWithMask.getLowestAddress(), "2001:db8:85a3::1"_ipv6); + EXPECT_EQ(netStringWithMask.getHighestAddress(), "2001:db8:85a3::ffff:ffff:ffff:ffff"_ipv6); + EXPECT_THROW(netStringWithMask.getTotalAddressCount(), std::out_of_range); + EXPECT_EQ(netStringWithMask.toString(), "2001:db8:85a3::/64"); + } + } + TEST(IPv6NetworkTest, IncludesMethod) + { + using namespace pcpp::literals; + + IPv6Network netBase("2001:db8:85a3:34ac::/64"); + + { + SCOPED_TRACE("With single address"); + + EXPECT_TRUE(netBase.includes("2001:db8:85a3:34ac::1"_ipv6)); + EXPECT_TRUE(netBase.includes("2001:db8:85a3:34ac:c::2"_ipv6)); + EXPECT_FALSE(netBase.includes("2001:db8:85a3:34ab::1"_ipv6)); + } + + { + SCOPED_TRACE("With network"); + + EXPECT_TRUE(netBase.includes(IPv6Network("2001:db8:85a3:34ac::/64"))); + EXPECT_TRUE(netBase.includes(IPv6Network("2001:db8:85a3:34ac::/72"))); + EXPECT_FALSE(netBase.includes(IPv6Network("2001:db8:85a3:34ac::/56"))); + } + }; + + TEST(IPv6NetworkTest, OutputStreamOperator) + { + using namespace pcpp::literals; + + IPv6Network net("2001:db8:85a3:34ac::/64"); + std::stringstream ss; + ss << net; + EXPECT_EQ(ss.str(), "2001:db8:85a3:34ac::/64"); + } + + TEST(IPNetworkTest, ConstructorWithSingleAddress) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("IPv4 address"); + + IPNetwork netSingleV4("192.168.1.1"_ipv4); + EXPECT_TRUE(netSingleV4.isIPv4Network()); + EXPECT_FALSE(netSingleV4.isIPv6Network()); + EXPECT_EQ(netSingleV4.getPrefixLen(), 32u); + EXPECT_EQ(netSingleV4.getNetmask(), "255.255.255.255"); + EXPECT_EQ(netSingleV4.getNetworkPrefix(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingleV4.getLowestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingleV4.getHighestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netSingleV4.getTotalAddressCount(), 1); + EXPECT_EQ(netSingleV4.toString(), "192.168.1.1/32"); + } + { + SCOPED_TRACE("IPv6 address"); + + IPNetwork netSingleV6("2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_FALSE(netSingleV6.isIPv4Network()); + EXPECT_TRUE(netSingleV6.isIPv6Network()); + EXPECT_EQ(netSingleV6.getPrefixLen(), 128u); + EXPECT_EQ(netSingleV6.getNetmask(), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + EXPECT_EQ(netSingleV6.getNetworkPrefix(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingleV6.getLowestAddress(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingleV6.getHighestAddress(), "2001:db8:85a3::8a2e:370:7334"_ipv6); + EXPECT_EQ(netSingleV6.getTotalAddressCount(), 1); + EXPECT_EQ(netSingleV6.toString(), "2001:db8:85a3::8a2e:370:7334/128"); + } + } + + TEST(IPNetworkTest, ConstructorWithAddressAndPrefix) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("IPv4 address"); + + IPNetwork netPrefix("192.168.1.1"_ipv4, 24u); + EXPECT_EQ(netPrefix.getPrefixLen(), 24u); + EXPECT_EQ(netPrefix.getNetmask(), "255.255.255.0"); + EXPECT_EQ(netPrefix.getNetworkPrefix(), "192.168.1.0"_ipv4); + EXPECT_EQ(netPrefix.getLowestAddress(), "192.168.1.1"_ipv4); + EXPECT_EQ(netPrefix.getHighestAddress(), "192.168.1.254"_ipv4); + EXPECT_EQ(netPrefix.getTotalAddressCount(), 256); + EXPECT_EQ(netPrefix.toString(), "192.168.1.0/24"); + } + + { + SCOPED_TRACE("IPv6 address"); + + IPNetwork netPrefix("2001:db8:85a3::8a2e:370:7334"_ipv6, 96u); + EXPECT_EQ(netPrefix.getPrefixLen(), 96u); + EXPECT_EQ(netPrefix.getNetmask(), "ffff:ffff:ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netPrefix.getNetworkPrefix(), "2001:db8:85a3::8a2e:0:0"_ipv6); + EXPECT_EQ(netPrefix.getLowestAddress(), "2001:db8:85a3::8a2e:0:1"_ipv6); + EXPECT_EQ(netPrefix.getHighestAddress(), "2001:db8:85a3::8a2e:ffff:ffff"_ipv6); + EXPECT_EQ(netPrefix.getTotalAddressCount(), 4294967296ul); + EXPECT_EQ(netPrefix.toString(), "2001:db8:85a3::8a2e:0:0/96"); + } + } + + TEST(IPNetworkTest, ConstructorWithAddressAndNetmask) + { + using namespace pcpp::literals; + + { + SCOPED_TRACE("IPv4 address"); + + IPNetwork netNetmask("192.168.1.1"_ipv4, "255.255.0.0"); + EXPECT_EQ(netNetmask.getPrefixLen(), 16u); + EXPECT_EQ(netNetmask.getNetmask(), "255.255.0.0"); + EXPECT_EQ(netNetmask.getNetworkPrefix(), "192.168.0.0"_ipv4); + EXPECT_EQ(netNetmask.getLowestAddress(), "192.168.0.1"_ipv4); + EXPECT_EQ(netNetmask.getHighestAddress(), "192.168.255.254"_ipv4); + EXPECT_EQ(netNetmask.getTotalAddressCount(), 256 * 256); + EXPECT_EQ(netNetmask.toString(), "192.168.0.0/16"); + } + + { + SCOPED_TRACE("IPv6 address"); + + IPNetwork netNetmask("2001:db8:85a3::8a2e:370:7334"_ipv6, "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netNetmask.getPrefixLen(), 64u); + EXPECT_EQ(netNetmask.getNetmask(), "ffff:ffff:ffff:ffff::"); + EXPECT_EQ(netNetmask.getNetworkPrefix(), "2001:db8:85a3::"_ipv6); + EXPECT_EQ(netNetmask.getLowestAddress(), "2001:db8:85a3::1"_ipv6); + EXPECT_EQ(netNetmask.getHighestAddress(), "2001:db8:85a3::ffff:ffff:ffff:ffff"_ipv6); + EXPECT_THROW(netNetmask.getTotalAddressCount(), std::out_of_range); + EXPECT_EQ(netNetmask.toString(), "2001:db8:85a3::/64"); + } + } + + TEST(IPNetworkTest, IncludesMethodWithIPv4) + { + using namespace pcpp::literals; + + IPNetwork netBaseV4("192.168.0.0/16"); + EXPECT_TRUE(netBaseV4.includes("192.168.1.0"_ipv4)); + EXPECT_TRUE(netBaseV4.includes("192.168.1.1"_ipv4)); + EXPECT_TRUE(netBaseV4.includes("192.168.2.1"_ipv4)); + EXPECT_FALSE(netBaseV4.includes("192.169.2.1"_ipv4)); + EXPECT_FALSE(netBaseV4.includes("2001:db8:85a3:34ac::"_ipv6)); + EXPECT_FALSE(netBaseV4.includes("::C0A9:0201"_ipv6)) << "IPNetwork in V4 mode should not match V6 equivalents."; + + EXPECT_TRUE(netBaseV4.includes(IPNetwork("192.168.1.0/24"))); + EXPECT_TRUE(netBaseV4.includes(IPNetwork("192.168.2.0/24"))); + EXPECT_TRUE(netBaseV4.includes(IPNetwork("192.168.0.0/16"))); + EXPECT_FALSE(netBaseV4.includes(IPNetwork("192.0.0.0/8"))); + EXPECT_FALSE(netBaseV4.includes(IPNetwork("2001:db8:85a3:34ac::/64"))); + EXPECT_FALSE(netBaseV4.includes(IPNetwork("::c0a9:0000/112"))) + << "IPNetwork in V4 mode should not match V6 equivalents."; + EXPECT_FALSE(netBaseV4.includes(IPNetwork("::c0a9:0201/116"))) + << "IPNetwork in V4 mode should not match V6 equivalents."; + } + + TEST(IPNetworkTest, IncludesMethodWithIPv6) + { + using namespace pcpp::literals; + + IPNetwork netBaseV6("2001:db8:85a3:34ac::/64"); + EXPECT_TRUE(netBaseV6.includes("2001:db8:85a3:34ac::1"_ipv6)); + EXPECT_TRUE(netBaseV6.includes("2001:db8:85a3:34ac:c::2"_ipv6)); + EXPECT_FALSE(netBaseV6.includes("2001:db8:85a3:34ab::1"_ipv6)); + + EXPECT_TRUE(netBaseV6.includes(IPNetwork("2001:db8:85a3:34ac::/64"))); + EXPECT_TRUE(netBaseV6.includes(IPNetwork("2001:db8:85a3:34ac::/72"))); + EXPECT_FALSE(netBaseV6.includes(IPNetwork("2001:db8:85a3:34ac::/56"))); + + IPNetwork netBaseV6_V4compat("::c0a8:0000/112"); + EXPECT_FALSE(netBaseV6_V4compat.includes("192.168.1.0"_ipv4)) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + EXPECT_FALSE(netBaseV6_V4compat.includes("192.168.2.1"_ipv4)) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + EXPECT_FALSE(netBaseV6_V4compat.includes("192.169.2.1"_ipv4)) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + + EXPECT_FALSE(netBaseV6_V4compat.includes(IPNetwork("192.169.1.1/15"))) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + EXPECT_FALSE(netBaseV6_V4compat.includes(IPNetwork("192.169.1.1/16"))) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + EXPECT_FALSE(netBaseV6_V4compat.includes(IPNetwork("192.169.1.1/17"))) + << "IPNetwork in V6 mode should not match V4 equivalent ranges."; + } + + TEST(IPNetworkTest, OutputStreamOperator) + { + IPv4Network netV4("192.168.1.1/32"); + std::stringstream ss; + ss << netV4; + EXPECT_EQ(ss.str(), "192.168.1.1/32"); + + ss.str(""); + IPNetwork netV6("2001:db8:85a3:34ac::/64"); + ss << netV6; + EXPECT_EQ(ss.str(), "2001:db8:85a3:34ac::/64"); + } +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/LRUListTests.cpp b/Tests/Common++Test/Tests/LRUListTests.cpp new file mode 100644 index 0000000000..e8a227f5a3 --- /dev/null +++ b/Tests/Common++Test/Tests/LRUListTests.cpp @@ -0,0 +1,86 @@ +#include "pch.h" + +#include "LRUList.h" + +namespace pcpp +{ + TEST(LRUListTest, PutTest) + { + LRUList lruList(3); + int deletedValue; + + // Test inserting elements + EXPECT_EQ(lruList.put(1), 0); + EXPECT_EQ(lruList.put(2), 0); + EXPECT_EQ(lruList.put(3), 0); + EXPECT_EQ(lruList.getSize(), 3); + + // Test inserting an element that exceeds the max size + EXPECT_EQ(lruList.put(4, &deletedValue), 1); + EXPECT_EQ(deletedValue, 1); + EXPECT_EQ(lruList.getSize(), 3); + + // Test inserting an existing element + EXPECT_EQ(lruList.put(2), 0); + EXPECT_EQ(lruList.getSize(), 3); + } + + TEST(LRUListTest, GetTest) + { + LRUList lruList(2); + + lruList.put("first"); + lruList.put("second"); + + // Test getting the most recently used element + EXPECT_EQ(lruList.getMRUElement(), "second"); + + // Test getting the least recently used element + EXPECT_EQ(lruList.getLRUElement(), "first"); + + lruList.put("third"); + + // Test getting the new most recently used element + EXPECT_EQ(lruList.getMRUElement(), "third"); + + // Test getting the new least recently used element + EXPECT_EQ(lruList.getLRUElement(), "second"); + } + + TEST(LRUListTest, EraseTest) + { + LRUList lruList(3); + + lruList.put(1); + lruList.put(2); + lruList.put(3); + + // Test erasing an element + lruList.eraseElement(2); + EXPECT_EQ(lruList.getSize(), 2); + + // Test erasing a non-existing element + lruList.eraseElement(4); + EXPECT_EQ(lruList.getSize(), 2); + } + + TEST(LRUListTest, SizeTest) + { + LRUList lruList(3); + + // Test initial size + EXPECT_EQ(lruList.getSize(), 0); + + lruList.put(1); + lruList.put(2); + + // Test size after inserting elements + EXPECT_EQ(lruList.getSize(), 2); + + lruList.put(3); + lruList.put(4); + + // Test size after exceeding max size + EXPECT_EQ(lruList.getSize(), 3); + } +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/LoggerTests.cpp b/Tests/Common++Test/Tests/LoggerTests.cpp new file mode 100644 index 0000000000..fa8c9a9170 --- /dev/null +++ b/Tests/Common++Test/Tests/LoggerTests.cpp @@ -0,0 +1,214 @@ +#include "pch.h" + +#include +#include +#include "Logger.h" + +namespace pcpp +{ + constexpr char getPathSeparator() + { +#ifdef _WIN32 + return '\\'; +#else + return '/'; +#endif // _WIN32 + } + + class LogCallbackMock + { + public: + MOCK_METHOD(void, call, + (pcpp::LogLevel logLevel, const std::string& logMessage, const std::string& fileName, + const std::string& method, const int line), + (const)); + + // Redirects the call to the mock method + void operator()(pcpp::LogLevel logLevel, const std::string& logMessage, const std::string& fileName, + const std::string& method, const int line) const + { + call(logLevel, logMessage, fileName, method, line); + } + }; + + class LoggerTest : public ::testing::Test + { + using Base = ::testing::Test; + + public: + LoggerTest() : m_Logger(Logger::getInstance()) + {} + + void SetUp() override + { + Base::SetUp(); + + // Setup log callback mock + m_LogCallbackMock = std::make_unique(); + + // Setup log callback to the mock + m_Logger.setLogPrinter(std::cref(*m_LogCallbackMock)); + + // Enable all logs and set them to Info level by default + m_Logger.enableLogs(); + m_Logger.setAllModulesToLogLevel(LogLevel::Info); + } + + void TearDown() override + { + // Reset log callback + m_Logger.enableLogs(); + m_Logger.setAllModulesToLogLevel(LogLevel::Info); + m_Logger.resetLogPrinter(); + + // Reset log callback mock + m_LogCallbackMock.reset(); + + Base::TearDown(); + } + + protected: +// Spoofing the log module for testing purposes +#pragma push_macro("LOG_MODULE") +#undef LOG_MODULE +#define LOG_MODULE ::pcpp::LogModule::PacketLogModuleArpLayer + + void invokeDebugLog(std::string const& msg) + { + PCPP_LOG_DEBUG(msg); + } + + void invokeErrorLog(std::string const& msg) + { + PCPP_LOG_ERROR(msg); + } + + static const LogModule SpoofedLogModule = LOG_MODULE; + +#pragma pop_macro("LOG_MODULE") + + Logger& m_Logger; + std::unique_ptr m_LogCallbackMock; + }; + + TEST_F(LoggerTest, LogLevelAsString) + { + EXPECT_EQ(Logger::logLevelAsString(LogLevel::Error), "ERROR"); + EXPECT_EQ(Logger::logLevelAsString(LogLevel::Info), "INFO"); + EXPECT_EQ(Logger::logLevelAsString(LogLevel::Debug), "DEBUG"); + } + + TEST_F(LoggerTest, GetSetLogLevel) + { + EXPECT_EQ(m_Logger.getLogLevel(SpoofedLogModule), LogLevel::Info) + << "Initial setup should have initialized all modules to Info"; + + m_Logger.setLogLevel(SpoofedLogModule, Logger::Debug); + EXPECT_EQ(m_Logger.getLogLevel(SpoofedLogModule), LogLevel::Debug); + EXPECT_TRUE(m_Logger.isDebugEnabled(SpoofedLogModule)); + } + + TEST_F(LoggerTest, SetAllModulesMethod) + { + for (int module = 1; module < NumOfLogModules; module++) + { + ASSERT_EQ(m_Logger.getLogLevel(static_cast(module)), LogLevel::Info); + } + + m_Logger.setAllModulesToLogLevel(LogLevel::Debug); + + for (int module = 1; module < NumOfLogModules; module++) + { + EXPECT_EQ(m_Logger.getLogLevel(static_cast(module)), LogLevel::Debug); + } + } + + TEST_F(LoggerTest, LogError) + { + using testing::_; + + ASSERT_EQ(m_Logger.getLogLevel(SpoofedLogModule), LogLevel::Info) + << "Initial setup should have initialized all modules to Info"; + + // Expect a call to the log callback mock + EXPECT_CALL(*m_LogCallbackMock, + call(LogLevel::Error, "Error Log Message", _ /* Filename */, _ /* method */, _ /* line number */)) + .Times(1); + + invokeErrorLog("Error Log Message"); + } + + TEST_F(LoggerTest, LogDebug) + { + using testing::_; + + m_Logger.setLogLevel(SpoofedLogModule, LogLevel::Debug); + ASSERT_EQ(m_Logger.getLogLevel(SpoofedLogModule), LogLevel::Debug); + + // Expect a call to the log callback mock + EXPECT_CALL(*m_LogCallbackMock, + call(LogLevel::Debug, "Debug Log Message", _ /* Filename */, _ /* method */, _ /* line number */)) + .Times(1); + + invokeDebugLog("Debug Log Message"); + } + + TEST_F(LoggerTest, GlobalLogSuppression) + { + using testing::_; + + m_Logger.suppressLogs(); + EXPECT_FALSE(m_Logger.logsEnabled()); + + // Expect no calls to the log callback mock + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Debug, "Global Log Suppression Error", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(0); + + invokeErrorLog("Global Log Suppression Error"); + + // Verifies that all expectations on the mock have been met and clears them. + ::testing::Mock::VerifyAndClearExpectations(m_LogCallbackMock.get()); + + m_Logger.enableLogs(); + EXPECT_TRUE(m_Logger.logsEnabled()); + + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Error, "Global Log Suppression Error", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(1); + + invokeErrorLog("Global Log Suppression Error"); + } + + TEST_F(LoggerTest, ModuleLevelLogSuppression) + { + using ::testing::_; + + m_Logger.setLogLevel(SpoofedLogModule, LogLevel::Error); + + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Debug, "Module Level Log Suppression Debug", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(0); + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Error, "Module Level Log Suppression Error", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(1); + + invokeDebugLog("Module Level Log Suppression Debug"); + invokeErrorLog("Module Level Log Suppression Error"); + + // Verifies that all expectations on the mock have been met and clears them. + ::testing::Mock::VerifyAndClearExpectations(m_LogCallbackMock.get()); + + m_Logger.setLogLevel(SpoofedLogModule, LogLevel::Debug); + + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Debug, "Module Level Log Suppression Debug", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(1); + EXPECT_CALL(*m_LogCallbackMock, call(LogLevel::Error, "Module Level Log Suppression Error", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(1); + + invokeDebugLog("Module Level Log Suppression Debug"); + invokeErrorLog("Module Level Log Suppression Error"); + } +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/MacAddressTests.cpp b/Tests/Common++Test/Tests/MacAddressTests.cpp new file mode 100644 index 0000000000..fd58bbaeaa --- /dev/null +++ b/Tests/Common++Test/Tests/MacAddressTests.cpp @@ -0,0 +1,160 @@ +#include "pch.h" + +#include +#include + +#include "MacAddress.h" + +namespace pcpp +{ + TEST(MacAddressTest, DefaultConstructor) + { + pcpp::MacAddress mac; + std::array expected = { 0, 0, 0, 0, 0, 0 }; + EXPECT_EQ(std::memcmp(mac.getRawData(), expected.data(), 6), 0); + } + + TEST(MacAddressTest, ByteArrayConstructor) + { + uint8_t addr[6] = { 1, 2, 3, 4, 5, 6 }; + pcpp::MacAddress mac(addr); + EXPECT_EQ(std::memcmp(mac.getRawData(), addr, 6), 0); + } + + TEST(MacAddressTest, StdArrayConstructor) + { + std::array addr = { 1, 2, 3, 4, 5, 6 }; + pcpp::MacAddress mac(addr); + EXPECT_EQ(std::memcmp(mac.getRawData(), addr.data(), 6), 0); + } + + TEST(MacAddressTest, StringConstructor) + { + std::string addr = "01:02:03:04:05:06"; + pcpp::MacAddress mac(addr); + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(std::memcmp(mac.getRawData(), expected.data(), 6), 0); + + EXPECT_THROW(pcpp::MacAddress("01:02:03:04:05"), std::invalid_argument); + EXPECT_THROW(pcpp::MacAddress("01:02:03:04:05:06:07"), std::invalid_argument); + EXPECT_THROW(pcpp::MacAddress("bogus string"), std::invalid_argument); + } + + TEST(MacAddressTest, OctetConstructor) + { + pcpp::MacAddress mac(1, 2, 3, 4, 5, 6); + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(std::memcmp(mac.getRawData(), expected.data(), 6), 0); + } + + TEST(MacAddressTest, InitializerListConstructor) + { + pcpp::MacAddress mac({ 1, 2, 3, 4, 5, 6 }); + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(std::memcmp(mac.getRawData(), expected.data(), 6), 0); + } + + TEST(MacAddressTest, EqualityOperator) + { + pcpp::MacAddress mac1(1, 2, 3, 4, 5, 6); + pcpp::MacAddress mac2(1, 2, 3, 4, 5, 6); + EXPECT_TRUE(mac1 == mac2); + + pcpp::MacAddress mac3(1, 2, 3, 4, 5, 7); + EXPECT_FALSE(mac1 == mac3); + } + + TEST(MacAddressTest, InequalityOperator) + { + pcpp::MacAddress mac1(1, 2, 3, 4, 5, 6); + pcpp::MacAddress mac2(1, 2, 3, 4, 5, 7); + EXPECT_TRUE(mac1 != mac2); + + pcpp::MacAddress mac3(1, 2, 3, 4, 5, 6); + EXPECT_FALSE(mac1 != mac3); + } + + TEST(MacAddressTest, AssignmentOperator) + { + pcpp::MacAddress mac; + mac = { 1, 2, 3, 4, 5, 6 }; + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(std::memcmp(mac.getRawData(), expected.data(), 6), 0); + } + + TEST(MacAddressTest, ToString) + { + pcpp::MacAddress mac(1, 2, 3, 4, 5, 6); + EXPECT_EQ(mac.toString(), "01:02:03:04:05:06"); + } + + TEST(MacAddressTest, CopyToBuffer) + { + pcpp::MacAddress macAddr(1, 2, 3, 4, 5, 6); + + constexpr size_t expectedRequiredBytes = 6; + std::array expected = { 1, 2, 3, 4, 5, 6 }; + + // Test query mode + EXPECT_EQ(macAddr.copyTo(nullptr, 0), 6); + + // Test with null buffer and non-zero size + EXPECT_THROW(macAddr.copyTo(nullptr, 1), std::invalid_argument); + + std::array buffer{}; + + // Test with smaller buffer. + EXPECT_EQ(macAddr.copyTo(buffer.data(), 5), expectedRequiredBytes); + EXPECT_THAT(buffer, ::testing::Each(::testing::Eq(0))); + + // Test with precise buffer + buffer.fill(0); + EXPECT_EQ(macAddr.copyTo(buffer.data(), expectedRequiredBytes), expectedRequiredBytes); + EXPECT_EQ(std::memcmp(buffer.data(), expected.data(), expectedRequiredBytes), 0); + EXPECT_TRUE(std::all_of(buffer.begin() + 6, buffer.end(), [](uint8_t x) { return x == 0; })); + + // Test with a buffer that is larger + buffer.fill(0); + EXPECT_EQ(macAddr.copyTo(buffer.data(), buffer.size()), expectedRequiredBytes); + EXPECT_EQ(std::memcmp(buffer.data(), expected.data(), expectedRequiredBytes), 0); + EXPECT_TRUE(std::all_of(buffer.begin() + 6, buffer.end(), [](uint8_t x) { return x == 0; })); + } + + TEST(MacAddressTest, CopyToNewBuffer) + { + pcpp::MacAddress macAddr(1, 2, 3, 4, 5, 6); + + constexpr size_t expectedRequiredBytes = 6; + std::array expected = { 1, 2, 3, 4, 5, 6 }; + + uint8_t* newBuffer = nullptr; + size_t newBufferSize = 0; + + EXPECT_THROW(macAddr.copyToNewBuffer(nullptr, newBufferSize), std::invalid_argument) + << "IPv6Address::copyToNewBuffer does not throw for null buffer pointer."; + + EXPECT_TRUE(macAddr.copyToNewBuffer(&newBuffer, newBufferSize)); + std::unique_ptr bufferGuard(newBuffer); + + ASSERT_NE(newBuffer, nullptr) << "IPv6Address::copyToNewBuffer did not allocate a new buffer."; + ASSERT_EQ(newBufferSize, expectedRequiredBytes) + << "IPv6Address::copyToNewBuffer did not return the correct size."; + + EXPECT_EQ(std::memcmp(newBuffer, expected.data(), expectedRequiredBytes), 0) + << "IPv6Address::copyToNewBuffer did not copy the address correctly."; + } + + TEST(MacAddressTest, OutputStreamOperator) + { + MacAddress macAddr(1, 2, 3, 4, 5, 6); + std::stringstream stream; + stream << macAddr; + EXPECT_EQ(stream.str(), "01:02:03:04:05:06"); + }; + + TEST(MacAddressTest, ConstantHelpers) + { + EXPECT_EQ(MacAddress::Zero, MacAddress(0, 0, 0, 0, 0, 0)); + EXPECT_EQ(MacAddress::Broadcast, MacAddress(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); + }; +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/ObjectPoolTests.cpp b/Tests/Common++Test/Tests/ObjectPoolTests.cpp new file mode 100644 index 0000000000..9bffa96e5d --- /dev/null +++ b/Tests/Common++Test/Tests/ObjectPoolTests.cpp @@ -0,0 +1,51 @@ +#include "pch.h" + +#include "ObjectPool.h" + +namespace pcpp +{ + namespace internal + { + + TEST(DynamicObjectPoolTest, BasicUsage) + { + DynamicObjectPool pool(10); // Create a pool with limit 10 objects + EXPECT_EQ(pool.maxSize(), 10); + EXPECT_EQ(pool.size(), 0); + + auto obj1 = pool.acquireObject(); // Acquire an object from the pool + EXPECT_NE(obj1, nullptr); + EXPECT_EQ(*obj1, 0); // Default initialized to 0 + EXPECT_EQ(pool.size(), 0); + *obj1 = 42; // Modify the object + + // Save the address of the object before releasing it + int* obj1Raw = obj1.get(); + pool.releaseObject(std::move(obj1)); + EXPECT_EQ(pool.size(), 1); + + auto obj2 = pool.acquireObject(); + EXPECT_EQ(obj1Raw, obj2.get()); // Should return the same object + pool.clear(); // Clear the pool + } + + TEST(DynamicObjectPoolTest, Preallocation) + { + DynamicObjectPool pool(10, 5); // Create a pool with limit 10 and preallocate 5 objects + EXPECT_EQ(pool.maxSize(), 10); + EXPECT_EQ(pool.size(), 5); + } + + TEST(DynamicObjectPoolTest, MaxPoolSize) + { + DynamicObjectPool pool(2); // Create a pool with limit 2 objects + + for (int i = 0; i < 4; ++i) + { + auto obj = std::make_unique(i); + pool.releaseObject(std::move(obj)); // Release objects to the pool + EXPECT_LE(pool.size(), 2); // Pool should free released objects if it exceeds the limit + } + } + } // namespace internal +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/PointerVectorTests.cpp b/Tests/Common++Test/Tests/PointerVectorTests.cpp new file mode 100644 index 0000000000..bf0d419675 --- /dev/null +++ b/Tests/Common++Test/Tests/PointerVectorTests.cpp @@ -0,0 +1,172 @@ +#include "pch.h" + +#include +#include + +#include "PointerVector.h" + +namespace pcpp +{ + class TestObject + { + public: + explicit TestObject(int value) : m_Value(value) + {} + int getValue() const + { + return m_Value; + } + std::unique_ptr clone() const + { + return std::unique_ptr(new TestObject(*this)); + } + + private: + int m_Value; + }; + + TEST(PointerVectorTest, DefaultConstructor) + { + pcpp::PointerVector vec; + EXPECT_EQ(vec.size(), 0); + } + + TEST(PointerVectorTest, CopyConstructor) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + pcpp::PointerVector copyVec(vec); + EXPECT_EQ(copyVec.size(), 2); + EXPECT_EQ(copyVec.at(0)->getValue(), 1); + EXPECT_EQ(copyVec.at(1)->getValue(), 2); + } + + TEST(PointerVectorTest, MoveConstructor) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + pcpp::PointerVector movedVec(std::move(vec)); + EXPECT_EQ(movedVec.size(), 2); + EXPECT_EQ(movedVec.at(0)->getValue(), 1); + EXPECT_EQ(movedVec.at(1)->getValue(), 2); + EXPECT_EQ(vec.size(), 0); + } + + TEST(PointerVectorTest, CopyAssignmentOperator) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + pcpp::PointerVector copyVec; + copyVec = vec; + EXPECT_EQ(copyVec.size(), 2); + EXPECT_EQ(copyVec.at(0)->getValue(), 1); + EXPECT_EQ(copyVec.at(1)->getValue(), 2); + } + + TEST(PointerVectorTest, MoveAssignmentOperator) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + pcpp::PointerVector movedVec; + movedVec = std::move(vec); + EXPECT_EQ(movedVec.size(), 2); + EXPECT_EQ(movedVec.at(0)->getValue(), 1); + EXPECT_EQ(movedVec.at(1)->getValue(), 2); + EXPECT_EQ(vec.size(), 0); + } + + TEST(PointerVectorTest, PushBack) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec.at(0)->getValue(), 1); + EXPECT_EQ(vec.at(1)->getValue(), 2); + } + + TEST(PointerVectorTest, PushBackUniquePtr) + { + pcpp::PointerVector vec; + vec.pushBack(std::unique_ptr(new TestObject(1))); + vec.pushBack(std::unique_ptr(new TestObject(2))); + + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec.at(0)->getValue(), 1); + EXPECT_EQ(vec.at(1)->getValue(), 2); + } + + TEST(PointerVectorTest, Clear) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + vec.clear(); + + EXPECT_EQ(vec.size(), 0); + } + + TEST(PointerVectorTest, Erase) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + vec.pushBack(new TestObject(3)); + + auto it = vec.begin(); + ++it; + vec.erase(it); + + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec.at(0)->getValue(), 1); + EXPECT_EQ(vec.at(1)->getValue(), 3); + } + + TEST(PointerVectorTest, GetAndDetach) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + auto obj = vec.getAndDetach(0); + EXPECT_EQ(obj->getValue(), 1); + EXPECT_EQ(vec.size(), 1); + EXPECT_EQ(vec.at(0)->getValue(), 2); + } + + TEST(PointerVectorTest, At) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + EXPECT_EQ(vec.at(0)->getValue(), 1); + EXPECT_EQ(vec.at(1)->getValue(), 2); + } + + TEST(PointerVectorTest, FrontBack) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + + EXPECT_EQ(vec.front()->getValue(), 1); + EXPECT_EQ(vec.back()->getValue(), 2); + } + + TEST(PointerVectorTest, PushBackNullptr) + { + pcpp::PointerVector vec; + TestObject* obj = nullptr; // Using nullptr directly in pushBack is a compile time error. + EXPECT_THROW(vec.pushBack(obj), std::invalid_argument); + } +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/SystemUtilsTests.cpp b/Tests/Common++Test/Tests/SystemUtilsTests.cpp new file mode 100644 index 0000000000..8bba647c15 --- /dev/null +++ b/Tests/Common++Test/Tests/SystemUtilsTests.cpp @@ -0,0 +1,46 @@ +#include "pch.h" + +#include "SystemUtils.h" + +namespace pcpp +{ + + TEST(CoreMaskTest, CreateCoreMaskFromCoreIdsMethod) + { + const std::vector coreIds{ 0, 1, 2, 3 }; + CoreMask mask = createCoreMaskFromCoreIds(coreIds); + + EXPECT_EQ(mask, 0x0F); + }; + + TEST(CoreMaskTest, CreateCoreMaskFromCoreVectorMethod) + { + + const std::vector cores{ + SystemCores::Core0, + SystemCores::Core1, + SystemCores::Core2, + SystemCores::Core3, + }; + + CoreMask mask = createCoreMaskFromCoreVector(cores); + + EXPECT_EQ(mask, 0x0F); + }; + + TEST(CoreMaskTest, CreateCoreVectorFromCoreMaskMethod) + { + const CoreMask mask = 0x0F; + const std::vector expectedCores = { + SystemCores::Core0, + SystemCores::Core1, + SystemCores::Core2, + SystemCores::Core3, + }; + std::vector cores; + + createCoreVectorFromCoreMask(mask, cores); + + EXPECT_EQ(cores, expectedCores); + }; +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/TimespecTimevalTests.cpp b/Tests/Common++Test/Tests/TimespecTimevalTests.cpp new file mode 100644 index 0000000000..0a0b507d57 --- /dev/null +++ b/Tests/Common++Test/Tests/TimespecTimevalTests.cpp @@ -0,0 +1,51 @@ +#include "pch.h" + +#include "TimespecTimeval.h" + +namespace pcpp +{ + namespace internal + { + TEST(TimespecTimevalConversion, TimevalToTimespecMacro) + { + timeval tv; + tv.tv_sec = 5000; + tv.tv_usec = 45888; + timespec ts; + TIMEVAL_TO_TIMESPEC(&tv, &ts); + EXPECT_EQ(ts.tv_sec, tv.tv_sec); + EXPECT_EQ(ts.tv_nsec, tv.tv_usec * 1000); + } + + TEST(TimespecTimevalConversion, TimespecToTimespecMacro) + { + timespec ts; + ts.tv_sec = 5000; + ts.tv_nsec = 45888; + timeval tv; + TIMESPEC_TO_TIMEVAL(&tv, &ts); + EXPECT_EQ(tv.tv_sec, ts.tv_sec); + EXPECT_EQ(tv.tv_usec, ts.tv_nsec / 1000); + } + + TEST(TimespecTimevalConversion, TimevalToTimespec) + { + timeval tv; + tv.tv_sec = 5000; + tv.tv_usec = 45; + timespec ts = toTimespec(tv); + EXPECT_EQ(ts.tv_sec, tv.tv_sec); + EXPECT_EQ(ts.tv_nsec, tv.tv_usec * 1000); + } + + TEST(TimespecTimevalConversion, TimespecToTimeval) + { + timespec ts; + ts.tv_sec = 5000; + ts.tv_nsec = 45888; + timeval tv = toTimeval(ts); + EXPECT_EQ(tv.tv_sec, ts.tv_sec); + EXPECT_EQ(tv.tv_usec, ts.tv_nsec / 1000); + } + } // namespace internal +} // namespace pcpp diff --git a/Tests/Common++Test/Utils/MemoryLeakListener.cpp b/Tests/Common++Test/Utils/MemoryLeakListener.cpp new file mode 100644 index 0000000000..4614e278e0 --- /dev/null +++ b/Tests/Common++Test/Utils/MemoryLeakListener.cpp @@ -0,0 +1,29 @@ +#include "MemoryLeakListener.hpp" + +#include + +namespace pcpp +{ + namespace test + { + void MemoryLeakListener::OnTestStart(const ::testing::TestInfo& testInfo) + { + MemPlumber::start(); + } + + void MemoryLeakListener::OnTestEnd(const ::testing::TestInfo& testInfo) + { + std::size_t memLeakCount = 0; + std::uint64_t memLeakSize = 0; + MemPlumber::memLeakCheck(memLeakCount, memLeakSize); + // TODO: This causes issues because it frees memory that might be used by the test framework + MemPlumber::stopAndFreeAllMemory(); + + if (memLeakCount > 0 || memLeakSize > 0) + { + FAIL() << "Memory leak found! " << memLeakCount << " objects and " << memLeakSize << " [bytes] leaked"; + } + } + + } // namespace test +} // namespace pcpp diff --git a/Tests/Common++Test/Utils/MemoryLeakListener.hpp b/Tests/Common++Test/Utils/MemoryLeakListener.hpp new file mode 100644 index 0000000000..c530e3f6c3 --- /dev/null +++ b/Tests/Common++Test/Utils/MemoryLeakListener.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace pcpp +{ + namespace test + { + class MemoryLeakListener : public ::testing::EmptyTestEventListener + { + public: + void OnTestStart(const ::testing::TestInfo& testInfo) override; + void OnTestEnd(const ::testing::TestInfo& testInfo) override; + }; + } // namespace test + +} // namespace pcpp diff --git a/Tests/Common++Test/main.cpp b/Tests/Common++Test/main.cpp new file mode 100644 index 0000000000..75e11326b6 --- /dev/null +++ b/Tests/Common++Test/main.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +#include "Logger.h" +#include "PcapPlusPlusVersion.h" +#include "Utils/MemoryLeakListener.hpp" + +int main(int argc, char* argv[]) +{ + std::cout << "PcapPlusPlus Common++Test" + "\nPcapPlusPlus version: " + << pcpp::getPcapPlusPlusVersionFull() // + << "\nBuilt: " << pcpp::getBuildDateTime() // + << "\nBuilt from: " << pcpp::getGitInfo() << std::endl; + + ::testing::InitGoogleMock(&argc, argv); + + // The logger singleton looks like a memory leak. Invoke it before starting the memory check + // Disables context pooling to avoid false positives in the memory leak check, as the contexts persist in the pool. + pcpp::Logger::getInstance().useContextPooling(false); + +#ifdef NDEBUG + // TODO: Do we still need this? The issue seems to be closed? + // clang-format off + /* + std::cout + << "Disabling memory leak check in MSVC Release builds due to caching logic in stream objects that looks like a memory leak:\n" + " https://github.com/cpputest/cpputest/issues/786#issuecomment-148921958" + << std::endl; + */ + // clang-format on +#else + // GTest sometimes allocates memory? which isn't freed before TearDown is called causing false positives and + // crashes. + + // auto& eventListeners = ::testing::UnitTest::GetInstance()->listeners(); + // eventListeners.Append(new pcpp::test::MemoryLeakListener()); +#endif + + return RUN_ALL_TESTS(); +} diff --git a/Tests/Common++Test/pch.h b/Tests/Common++Test/pch.h new file mode 100644 index 0000000000..bb5a5e479b --- /dev/null +++ b/Tests/Common++Test/pch.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +#include +#include diff --git a/ci/run_tests/run_tests.py b/ci/run_tests/run_tests.py index df4f3fe373..f530d72208 100644 --- a/ci/run_tests/run_tests.py +++ b/ci/run_tests/run_tests.py @@ -21,6 +21,16 @@ def tcp_replay_worker(interface: str, tcpreplay_dir: str): tcpreplay_proc.kill() +def run_common_tests(args: list[str], use_sudo: bool): + cmd_line = ["sudo"] if use_sudo else [] + cmd_line += [os.path.join("Bin", "Common++Test"), *args] + + completed_process = subprocess.run(cmd_line, cwd="Tests/Common++Test") + + if completed_process.returncode != 0: + raise RuntimeError(f"Error while executing Common++ tests: {completed_process}") + + def run_packet_tests(args: list[str], use_sudo: bool): cmd_line = ["sudo"] if use_sudo else [] cmd_line += [os.path.join("Bin", "Packet++Test"), *args] @@ -56,10 +66,16 @@ def main(): "--test-suites", nargs="+", type=str, - default=["packet", "pcap"], - choices=["packet", "pcap"], + default=["common", "packet", "pcap"], + choices=["common", "packet", "pcap"], help="test suites to use", ) + parser.add_argument( + "--common-test-args", + type=str, + default="", + help="common++ test arguments", + ) parser.add_argument( "--packet-test-args", type=str, @@ -80,6 +96,9 @@ def main(): ) args = parser.parse_args() + if "common" in args.test_suites: + run_common_tests(args.common_test_args.split(), args.use_sudo) + if "packet" in args.test_suites: run_packet_tests(args.packet_test_args.split(), args.use_sudo) diff --git a/ci/run_tests/run_tests_windows.py b/ci/run_tests/run_tests_windows.py index 532241bfeb..9e2ed3d51c 100644 --- a/ci/run_tests/run_tests_windows.py +++ b/ci/run_tests/run_tests_windows.py @@ -62,6 +62,95 @@ def find_interface(): return None, None +def run_common_tests(): + return subprocess.run( + os.path.join("Bin", "Common++Test"), + cwd=os.path.join("Tests", "Common++Test"), + shell=True, + check=True, # Raise exception if the worker returns in non-zero status code + ) + + +def run_common_coverage(): + raise NotImplementedError + + +def run_packet_tests(): + return subprocess.run( + os.path.join("Bin", "Packet++Test"), + cwd=os.path.join("Tests", "Packet++Test"), + shell=True, + check=True, # Raise exception if the worker returns in non-zero status code + ) + + +def run_packet_coverage(): + return subprocess.run( + [ + "OpenCppCoverage.exe", + "--verbose", + "--sources", + "Packet++", + "--sources", + "Pcap++", + "--sources", + "Common++", + "--excluded_sources", + "Tests", + "--export_type", + "cobertura:Packet++Coverage.xml", + "--", + os.path.join("Bin", "Packet++Test"), + ], + cwd=os.path.join("Tests", "Packet++Test"), + shell=True, + check=True, # Raise exception if the worker returns in non-zero status code + ) + + +def run_pcap_tests(ip_address: str, skip_tests: list[str]): + return subprocess.run( + [ + os.path.join("Bin", "Pcap++Test"), + "-i", + ip_address, + "-x", + ";".join(skip_tests), + ], + cwd=os.path.join("Tests", "Pcap++Test"), + shell=True, + check=True, # Raise exception if the worker returns in non-zero status code + ) + + +def run_pcap_coverage(ip_address: str, skip_tests: list[str]): + return subprocess.run( + [ + "OpenCppCoverage.exe", + "--verbose", + "--sources", + "Packet++", + "--sources", + "Pcap++", + "--sources", + "Common++", + "--excluded_sources", + "Tests", + "--export_type", + "cobertura:Pcap++Coverage.xml", + "--", + os.path.join("Bin", "Pcap++Test"), + "-i", + ip_address, + "-x", + ";".join(skip_tests), + ], + cwd=os.path.join("Tests", "Pcap++Test"), + shell=True, + check=True, # Raise exception if the worker returns in non-zero status code + ) + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -87,84 +176,23 @@ def main(): exit(1) print(f"Interface is {tcpreplay_interface} and IP address is {ip_address}") + if args.coverage: + run_packet_coverage() + else: + run_common_tests() + run_packet_tests() + try: tcpreplay_cmd = ( f'tcpreplay.exe -i "{tcpreplay_interface}" --mbps=10 -l 0 {PCAP_FILE_PATH}' ) tcpreplay_proc = subprocess.Popen(tcpreplay_cmd, shell=True, cwd=TCPREPLAY_PATH) - if args.coverage: - completed_process = subprocess.run( - [ - "OpenCppCoverage.exe", - "--verbose", - "--sources", - "Packet++", - "--sources", - "Pcap++", - "--sources", - "Common++", - "--excluded_sources", - "Tests", - "--export_type", - "cobertura:Packet++Coverage.xml", - "--", - os.path.join("Bin", "Packet++Test"), - ], - cwd=os.path.join("Tests", "Packet++Test"), - shell=True, - ) - else: - completed_process = subprocess.run( - os.path.join("Bin", "Packet++Test"), - cwd=os.path.join("Tests", "Packet++Test"), - shell=True, - ) - if completed_process.returncode != 0: - print("Error while executing Packet++ tests: " + str(completed_process)) - exit(completed_process.returncode) - skip_tests = ["TestRemoteCapture"] + args.skip_tests if args.coverage: - completed_process = subprocess.run( - [ - "OpenCppCoverage.exe", - "--verbose", - "--sources", - "Packet++", - "--sources", - "Pcap++", - "--sources", - "Common++", - "--excluded_sources", - "Tests", - "--export_type", - "cobertura:Pcap++Coverage.xml", - "--", - os.path.join("Bin", "Pcap++Test"), - "-i", - ip_address, - "-x", - ";".join(skip_tests), - ], - cwd=os.path.join("Tests", "Pcap++Test"), - shell=True, - ) + run_pcap_coverage(ip_address, skip_tests) else: - completed_process = subprocess.run( - [ - os.path.join("Bin", "Pcap++Test"), - "-i", - ip_address, - "-x", - ";".join(skip_tests), - ], - cwd=os.path.join("Tests", "Pcap++Test"), - shell=True, - ) - if completed_process.returncode != 0: - print("Error while executing Pcap++ tests: " + str(completed_process)) - exit(completed_process.returncode) + run_pcap_tests(ip_address, skip_tests) finally: subprocess.call(["taskkill", "/F", "/T", "/PID", str(tcpreplay_proc.pid)])