Skip to content

Commit

Permalink
Test generators: fix and improve
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
leoossa committed Jan 16, 2025
1 parent d1ea30d commit 1510a94
Showing 1 changed file with 128 additions and 27 deletions.
155 changes: 128 additions & 27 deletions test/unittests/test_generators.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#endif

#include <algorithm>
#include <utility>

#include "openvpn/addr/ip.hpp"
#include "openvpn/tun/builder/capture.hpp"
Expand Down Expand Up @@ -140,20 +141,18 @@ inline auto hexChar(const bool valid = true) -> Gen<std::string>
{
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<char>(c)}; });
// "0" should be generated <probability_weight_of_0> times more often
return gen::map(gen::weightedOneOf<char>({{probability_weight_of_0, gen::just<char>('0')},
{probability_weight_of_alphapositives, alphapositives}}),
[](const auto &c)
{
return std::string{static_cast<char>(c)};
});
}

// Generate invalid hexadecimal characters
Expand All @@ -164,6 +163,7 @@ inline auto hexChar(const bool valid = true) -> Gen<std::string>
return std::string{static_cast<char>(c)};
});
}

/**
* @brief Generates a hextet value of an IPv6 address.
* @details This function generates a hextet (4 characters) value of an IPv6 address,
Expand Down Expand Up @@ -196,6 +196,94 @@ inline auto IPv6HextetValue(const bool valid = true) -> Gen<std::string>
});
}

/**
* @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<std::string> &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<std::string> &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<std::string> &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<std::string> 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
Expand All @@ -219,21 +307,34 @@ inline auto IPv6Address(const bool valid = true) -> Gen<std::string>
static constexpr std::array<bool, number_of_hextets> all_true = {true, true, true, true, true, true, true, true};
const auto hextet_validity = valid ? all_true : *atLeastOneFalse<number_of_hextets>().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;
Expand Down

0 comments on commit 1510a94

Please sign in to comment.