From 1510a94f8c5d25a4a89f6290cbd6d760efc4626d Mon Sep 17 00:00:00 2001 From: Leonard Ossa Date: Fri, 10 Jan 2025 14:31:30 +0100 Subject: [PATCH] Test generators: fix and improve hexChar for generating IPv6 generates now 0 with higher probability than other chars, it was also refactored for better legibility compression of generated IPv6 addresses was added IPv6Address generator was refactored for better legibility and fixed bug with valid parameters Signed-off-by: Leonard Ossa --- test/unittests/test_generators.h | 155 +++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 27 deletions(-) diff --git a/test/unittests/test_generators.h b/test/unittests/test_generators.h index fce52f5cd..d6ee532df 100644 --- a/test/unittests/test_generators.h +++ b/test/unittests/test_generators.h @@ -10,6 +10,7 @@ #endif #include +#include #include "openvpn/addr/ip.hpp" #include "openvpn/tun/builder/capture.hpp" @@ -140,20 +141,18 @@ inline auto hexChar(const bool valid = true) -> Gen { if (valid) { - static constexpr int ASCII_zero_position = 48; // '0' - static constexpr int ASCII_nine_position = 57; // '9' - const auto numbers = gen::inRange(ASCII_zero_position, ASCII_nine_position + 1); + const auto alphapositives = gen::elementOf(std::string("abcdefABCDEF123456789")); - static constexpr int ASCII_uppercase_a_position = 65; // represents 'A' - static constexpr int ASCII_uppercase_f_position = 70; // represents 'F' - const auto uppercase = gen::inRange(ASCII_uppercase_a_position, ASCII_uppercase_f_position + 1); + static constexpr int probability_weight_of_0 = 23; + static constexpr int probability_weight_of_alphapositives = 1; - static constexpr int ASCII_lowercase_a_position = 97; // represents 'a' - static constexpr int ASCII_lowercase_f_position = 102; // represents 'f' - const auto lowercase = gen::inRange(ASCII_lowercase_a_position, ASCII_lowercase_f_position + 1); - - return gen::map(gen::oneOf(numbers, uppercase, lowercase), [](const auto &c) - { return std::string{static_cast(c)}; }); + // "0" should be generated times more often + return gen::map(gen::weightedOneOf({{probability_weight_of_0, gen::just('0')}, + {probability_weight_of_alphapositives, alphapositives}}), + [](const auto &c) + { + return std::string{static_cast(c)}; + }); } // Generate invalid hexadecimal characters @@ -164,6 +163,7 @@ inline auto hexChar(const bool valid = true) -> Gen return std::string{static_cast(c)}; }); } + /** * @brief Generates a hextet value of an IPv6 address. * @details This function generates a hextet (4 characters) value of an IPv6 address, @@ -196,6 +196,94 @@ inline auto IPv6HextetValue(const bool valid = true) -> Gen }); } +/** + * @brief Removes leading zeros from a hextet (IPv6 segment). + * @details This function trims all leading zeros from a given hextet string. + * If the hextet only contains zeros (or is empty), it returns "0". + * @param hextet The input string representing a hextet (e.g., "0001", "0ABC"). + * @return A string without leading zeros. + */ +inline std::string removeLeadingZerosFromHextet(const std::string &hextet) +{ + const auto first_nonzero = hextet.find_first_not_of('0'); + return first_nonzero == std::string::npos ? "0" : hextet.substr(first_nonzero); +} + +/** + * @brief Removes leading zeros from a vector of hextets. + * @details This function modifies a vector of IPv6 hextets by removing leading zeros + * from each hextet using the `removeLeadingZerosFromHextet` transformation. + * The operation is performed in-place. + * @param hextets A reference to a vector of strings representing IPv6 hextets. + * Each hextet will be stripped of its leading zeros. + */ +inline void removeLeadingZerosFromHextets(std::vector &hextets) +{ + std::transform(hextets.begin(), hextets.end(), hextets.begin(), removeLeadingZerosFromHextet); +} + +/** + * @brief Replaces the longest sequence of consecutive "0" strings in a vector with "::". + * @details This function finds the longest contiguous sequence of "0" strings + * in the provided `hextets` vector, replaces it with a single "::", + * and removes the other "0" strings in the sequence. + * If there are multiple sequences of the same length, it acts on the first one found. + * @param hextets A vector of strings representing the hextets (subdivisions) of an IPv6 address. + */ +inline void replaceSequenceOfZerosWithDoubleColon(std::vector &hextets) +{ + for (auto longest_zero_sequence = hextets.size(); longest_zero_sequence > 1; --longest_zero_sequence) + { + auto position = std::search_n(hextets.begin(), hextets.end(), longest_zero_sequence, std::string{"0"}); + if (position != hextets.end()) + { + const auto it = hextets.insert(position, "::"); + hextets.erase(it + 1, std::next(it + 1, longest_zero_sequence)); + return; + } + } +} + +/** + * @brief Converts a vector of hextets to an IPv6 address string with colons. + * @details This function takes a vector of strings representing hextets (parts of an IPv6 address) + * and constructs a colon-separated IPv6 address. The function ensures that colons are + * correctly placed between the hextets, and it avoids adding redundant colons around "::" + * which represents a compressed sequence of zeroes in an IPv6 address. + * + * @param hextets A vector of strings, where each string represents a hextet or "::". + * @return A string representation of the IPv6 address with colons separating the hextets. + */ +inline std::string stringifyHextetsToAddressWithColons(const std::vector &hextets) +{ + std::string result; + for (size_t i = 0; i < hextets.size(); ++i) + { + if (i > 0 && hextets[i] != "::" && hextets[i - 1] != "::") + { + result += ":"; + } + result += hextets[i]; + } + return result; +} + +/** + * @brief Compress an IPv6 address by simplifying its representation. + * @details This function takes a list of hextets (hexadecimal segments of an IPv6 address), + * removes leading zeros from each hextet, replaces the largest contiguous sequence + * of zeroed hextets with a double colon (::), and converts the simplified hextets + * back into a string representation of the IPv6 address. + * @param hextets A vector of strings, where each string represents one hextet of an IPv6 address. + * @return A string containing the compressed IPv6 address. + */ +inline std::string compressIPv6Address(std::vector hextets) +{ + removeLeadingZerosFromHextets(hextets); + replaceSequenceOfZerosWithDoubleColon(hextets); + return stringifyHextetsToAddressWithColons(hextets); +} + /** * @brief Generates a random IPv6 address. * @details This function generates a random IPv6 address. The validity of the hextets @@ -219,21 +307,34 @@ inline auto IPv6Address(const bool valid = true) -> Gen static constexpr std::array all_true = {true, true, true, true, true, true, true, true}; const auto hextet_validity = valid ? all_true : *atLeastOneFalse().as("first,second,third,fourth,fifth,sixth,seventh,eighth hextet valid"); - return gen::oneOf(gen::map( - gen::tuple( - IPv6HextetValue(hextet_validity[0]), - IPv6HextetValue(hextet_validity[1]), - IPv6HextetValue(hextet_validity[2]), - IPv6HextetValue(hextet_validity[3]), - IPv6HextetValue(hextet_validity[4]), - IPv6HextetValue(hextet_validity[5]), - IPv6HextetValue(hextet_validity[6]), - IPv6HextetValue(hextet_validity[7])), - [](const auto &hextets) - { - const auto& [first_hextet, second_hextet, third_hextet, fourth_hextet, fifth_hextet, sixth_hextet, seventh_hextet, eighth_hextet] = hextets; - return first_hextet + ":" + second_hextet + ":" + third_hextet + ":" + fourth_hextet + ":" + fifth_hextet + ":" + sixth_hextet + ":" + seventh_hextet + ":" + eighth_hextet; }), - gen::just(std::string{"::0"}).as("valid IPv6 address")); + auto convert_hextets_to_compressed_address = [](std::string first_hextet, + std::string second_hextet, + std::string third_hextet, + std::string fourth_hextet, + std::string fifth_hextet, + std::string sixth_hextet, + std::string seventh_hextet, + std::string eighth_hextet) + { + return compressIPv6Address({std::move(first_hextet), + std::move(second_hextet), + std::move(third_hextet), + std::move(fourth_hextet), + std::move(fifth_hextet), + std::move(sixth_hextet), + std::move(seventh_hextet), + std::move(eighth_hextet)}); + }; + + return gen::apply(convert_hextets_to_compressed_address, + IPv6HextetValue(hextet_validity[0]), + IPv6HextetValue(hextet_validity[1]), + IPv6HextetValue(hextet_validity[2]), + IPv6HextetValue(hextet_validity[3]), + IPv6HextetValue(hextet_validity[4]), + IPv6HextetValue(hextet_validity[5]), + IPv6HextetValue(hextet_validity[6]), + IPv6HextetValue(hextet_validity[7])); } using RedirectGatewayFlagsValues = openvpn::RedirectGatewayFlags::Flags;