diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77ca6d1a3..105175541 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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++11", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force"] + args: ["--std=c++11", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force", "--library=googletest"] - repo: https://github.com/BlankSpruce/gersemi rev: 0.17.1 hooks: diff --git a/Common++/header/IpAddress.h b/Common++/header/IpAddress.h index 38936e857..813d2a4e9 100644 --- a/Common++/header/IpAddress.h +++ b/Common++/header/IpAddress.h @@ -439,7 +439,7 @@ namespace pcpp {} /// A constructor that creates an instance of the class out of an address representing the network prefix - /// and a prefix length + /// and a prefix length /// @param address An address representing the network prefix. If the address is invalid std::invalid_argument /// exception is thrown /// @param prefixLen A number between 0 and 32 representing the prefix length. @@ -841,6 +841,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 1cf5852e6..86b742f4d 100644 --- a/Common++/header/MacAddress.h +++ b/Common++/header/MacAddress.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -142,7 +143,7 @@ namespace pcpp } /// 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; private: std::array m_Address{}; diff --git a/Common++/src/MacAddress.cpp b/Common++/src/MacAddress.cpp index 18886faae..36739211b 100644 --- a/Common++/src/MacAddress.cpp +++ b/Common++/src/MacAddress.cpp @@ -3,7 +3,7 @@ namespace pcpp { - MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); + const MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); std::string MacAddress::toString() const { diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index b36ff25a4..0bfb577b9 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,4 +1,5 @@ if(PCAPPP_BUILD_TESTS) + add_subdirectory(Common++Test) add_subdirectory(Packet++Test) add_subdirectory(Pcap++Test) add_subdirectory(PcppTestFramework) diff --git a/Tests/Common++Test/CMakeLists.txt b/Tests/Common++Test/CMakeLists.txt new file mode 100644 index 000000000..fa0df6fc0 --- /dev/null +++ b/Tests/Common++Test/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.14) + +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.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/PointerVectorTests.cpp + Tests/SystemUtilsTests.cpp) + +target_link_libraries( + Common++Test + PRIVATE Common++ + memplumber + gtest + gmock) + +target_include_directories(Common++Test PRIVATE $) + +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 000000000..b56c49dc4 --- /dev/null +++ b/Tests/Common++Test/Tests/GeneralUtilsTests.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#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 000000000..c1d10f863 --- /dev/null +++ b/Tests/Common++Test/Tests/IPAddressTests.cpp @@ -0,0 +1,757 @@ +#include +#include +#include +#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, 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 000000000..86483b59b --- /dev/null +++ b/Tests/Common++Test/Tests/LRUListTests.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +#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 000000000..194782db0 --- /dev/null +++ b/Tests/Common++Test/Tests/LoggerTests.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#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::Logger::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::Logger::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 trampoline + m_Logger.setLogPrinter(&LoggerTest::logPrinterTrampoline); + + // Enable all logs and set them to Info level by default + m_Logger.enableLogs(); + m_Logger.setAllModulesToLogLevel(Logger::Info); + + // Setup log callback mock + m_LogCallbackMock = std::unique_ptr(new LogCallbackMock()); + } + + void TearDown() override + { + // Reset log callback trampoline + m_Logger.enableLogs(); + m_Logger.setAllModulesToLogLevel(Logger::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") + + static void logPrinterTrampoline(Logger::LogLevel logLevel, const std::string& logMessage, + const std::string& fileName, const std::string& method, const int line) + { + if (m_LogCallbackMock != nullptr) + { + // Dereference the pointer and call the mock with the parameters. + (*m_LogCallbackMock)(logLevel, logMessage, fileName, method, line); + } + else + { + throw std::runtime_error("Log Trampoline Error: Log callback not set"); + } + } + + static std::unique_ptr m_LogCallbackMock; + Logger& m_Logger; + }; + + std::unique_ptr LoggerTest::m_LogCallbackMock = nullptr; + + TEST_F(LoggerTest, LogLevelAsString) + { + EXPECT_EQ(Logger::logLevelAsString(Logger::Error), "ERROR"); + EXPECT_EQ(Logger::logLevelAsString(Logger::Info), "INFO"); + EXPECT_EQ(Logger::logLevelAsString(Logger::Debug), "DEBUG"); + } + + TEST_F(LoggerTest, GetSetLogLevel) + { + EXPECT_EQ(m_Logger.getLogLevel(SpoofedLogModule), Logger::Info) << "Initial setup should have initialized all modules to Info"; + + m_Logger.setLogLevel(SpoofedLogModule, Logger::Debug); + EXPECT_EQ(m_Logger.getLogLevel(SpoofedLogModule), Logger::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)), Logger::Info); + } + + m_Logger.setAllModulesToLogLevel(Logger::Debug); + + for (int module = 1; module < NumOfLogModules; module++) + { + EXPECT_EQ(m_Logger.getLogLevel(static_cast(module)), Logger::Debug); + } + } + + TEST_F(LoggerTest, LogError) + { + using testing::_; + + ASSERT_EQ(m_Logger.getLogLevel(SpoofedLogModule), Logger::Info) + << "Initial setup should have initialized all modules to Info"; + + // Expect a call to the log callback mock + EXPECT_CALL(*m_LogCallbackMock, + call(Logger::Error, "Error Log Message", _ /* Filename */, _ /* method */, _ /* line number */)) + .Times(1); + + invokeErrorLog("Error Log Message"); + } + + TEST_F(LoggerTest, LogDebug) + { + using testing::_; + + m_Logger.setLogLevel(SpoofedLogModule, Logger::Debug); + ASSERT_EQ(m_Logger.getLogLevel(SpoofedLogModule), Logger::Debug); + + // Expect a call to the log callback mock + EXPECT_CALL(*m_LogCallbackMock, + call(Logger::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(Logger::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(Logger::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, Logger::Error); + + EXPECT_CALL(*m_LogCallbackMock, call(Logger::Debug, "Module Level Log Suppression Debug", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(0); + EXPECT_CALL(*m_LogCallbackMock, call(Logger::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, Logger::Debug); + + EXPECT_CALL(*m_LogCallbackMock, call(Logger::Debug, "Module Level Log Suppression Debug", _ /* Filename */, + _ /* method */, _ /* line number */)) + .Times(1); + EXPECT_CALL(*m_LogCallbackMock, call(Logger::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 \ No newline at end of file diff --git a/Tests/Common++Test/Tests/MacAddressTests.cpp b/Tests/Common++Test/Tests/MacAddressTests.cpp new file mode 100644 index 000000000..eab5e5b07 --- /dev/null +++ b/Tests/Common++Test/Tests/MacAddressTests.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#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, CopyToAllocatedArray) + { + pcpp::MacAddress mac(1, 2, 3, 4, 5, 6); + uint8_t* arr = nullptr; + mac.copyTo(&arr); + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(std::memcmp(arr, expected.data(), 6), 0); + delete[] arr; + } + + TEST(MacAddressTest, CopyToPreAllocatedArray) + { + pcpp::MacAddress mac(1, 2, 3, 4, 5, 6); + std::array arr; + mac.copyTo(arr.data()); + std::array expected = { 1, 2, 3, 4, 5, 6 }; + EXPECT_EQ(arr, expected); + } + + 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)); + }; +} // namespace pcpp diff --git a/Tests/Common++Test/Tests/PointerVectorTests.cpp b/Tests/Common++Test/Tests/PointerVectorTests.cpp new file mode 100644 index 000000000..9299fef2a --- /dev/null +++ b/Tests/Common++Test/Tests/PointerVectorTests.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include + +#include "MemoryLeakDetectorFixture.hpp" + +#include "PointerVector.h" + +namespace pcpp +{ + class TestObject + { + public: + TestObject(int value) : m_Value(value) + {} + int getValue() const + { + return m_Value; + } + std::unique_ptr clone() const + { + return std::make_unique(*this); + } + + private: + int m_Value; + }; + + class PointerVectorTest : public MemoryLeakDetectorTest + { + }; + + TEST_F(PointerVectorTest, DefaultConstructor) + { + pcpp::PointerVector vec; + EXPECT_EQ(vec.size(), 0); + } + + TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(PointerVectorTest, Clear) + { + pcpp::PointerVector vec; + vec.pushBack(new TestObject(1)); + vec.pushBack(new TestObject(2)); + vec.clear(); + + EXPECT_EQ(vec.size(), 0); + } + + TEST_F(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_F(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_F(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_F(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_F(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 000000000..0b7fb31b6 --- /dev/null +++ b/Tests/Common++Test/Tests/SystemUtilsTests.cpp @@ -0,0 +1,47 @@ +#include +#include + +#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 \ No newline at end of file diff --git a/Tests/Common++Test/Utils/MemoryLeakDetectorFixture.hpp b/Tests/Common++Test/Utils/MemoryLeakDetectorFixture.hpp new file mode 100644 index 000000000..f1a50e999 --- /dev/null +++ b/Tests/Common++Test/Utils/MemoryLeakDetectorFixture.hpp @@ -0,0 +1,61 @@ +#include +#include + +#include + +namespace pcpp +{ + class MemoryLeakDetectorTest : public ::testing::Test + { + using Base = ::testing::Test; + + public: + static void SetUpTestSuite() + { +#ifdef NDEBUG + // TODO: Do we still need this? The issue seems to be closed? + skipMemLeakCheck = true; + std::call_once(m_MSVC_WarningPrinted, [] { + 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; + }); +#else + skipMemLeakCheck = false; +#endif + } + + protected: + void SetUp() override + { + Base::SetUp(); + + if (!skipMemLeakCheck) + { + MemPlumber::start(); + } + } + + void TearDown() override + { + std::size_t memLeakCount = 0; + std::uint64_t memLeakSize = 0; + MemPlumber::memLeakCheck(memLeakCount, memLeakSize); + MemPlumber::stopAndFreeAllMemory(); + + if (memLeakCount > 0 || memLeakSize > 0) + { + FAIL() << "Memory leak found! " << memLeakCount << " objects and " << memLeakSize << " [bytes] leaked"; + } + Base::TearDown(); + } + + private: + static bool skipMemLeakCheck; + static std::once_flag m_MSVC_WarningPrinted; + }; + + bool MemoryLeakDetectorTest::skipMemLeakCheck = false; + std::once_flag MemoryLeakDetectorTest::m_MSVC_WarningPrinted; +} // namespace pcpp diff --git a/Tests/Common++Test/main.cpp b/Tests/Common++Test/main.cpp new file mode 100644 index 000000000..9197bc093 --- /dev/null +++ b/Tests/Common++Test/main.cpp @@ -0,0 +1,16 @@ +#include + +#include +#include + +#include "PcapPlusPlusVersion.h" + +int main(int argc, char* argv[]) +{ + std::cout << "PcapPlusPlus version: " << pcpp::getPcapPlusPlusVersionFull() << '\n' + << "Built: " << pcpp::getBuildDateTime() << '\n' + << "Built from: " << pcpp::getGitInfo() << std::endl; + + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +}