OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_generators.hpp
Go to the documentation of this file.
1#ifndef TEST_GENERATORS_HPP
2#define TEST_GENERATORS_HPP
3#ifdef __GNUC__
4#pragma GCC diagnostic push
5#pragma GCC diagnostic ignored "-Wconversion" // turn off warning for rapidcheck
6#endif
7#include <rapidcheck/gtest.h>
8#ifdef __GNUC__
9#pragma GCC diagnostic pop
10#endif
11
12#include <algorithm>
13#include <utility>
14#include <string>
15#include <tuple>
16#include <array>
17#include <string_view>
18#include <bitset>
19#include <sstream>
20#include <vector>
21
22#include "openvpn/addr/ip.hpp"
24
25namespace rc {
26
39template <size_t N>
40auto atLeastOneFalse() -> Gen<std::array<bool, N>>
41{
42 static_assert(N > 0, "N must be greater than 0");
43
44 return gen::suchThat(
45 gen::container<std::array<bool, N>>(gen::arbitrary<bool>()),
46 [](const auto &booleans)
47 {
48 return std::any_of(booleans.begin(), booleans.end(), [](const bool b)
49 { return !b; });
50 });
51}
52
73template <size_t N>
74auto generateValidityFlags(const bool all_valid = true) -> Gen<std::array<bool, N>>
75{
76 if (all_valid)
77 {
78 return gen::container<std::array<bool, N>>(gen::just(true));
79 }
80 return atLeastOneFalse<N>();
81}
82
96inline auto IPv4Octet(const bool valid = true) -> Gen<int>
97{
98 static constexpr int min_ipv4_octet = 0;
99 static constexpr int max_ipv4_octet = 255;
100
101 if (valid)
102 {
103 return gen::inRange(min_ipv4_octet, max_ipv4_octet + 1);
104 }
105 return gen::suchThat(gen::arbitrary<int>(), [](const auto &i)
106 { return i < min_ipv4_octet || i > max_ipv4_octet; });
107}
108
125inline auto IPv4Address(const bool valid = true) -> Gen<std::string>
126{
127 static constexpr int octets_number = 4;
128 static constexpr std::array<bool, octets_number> all_true = {true, true, true, true};
129 const auto octet_validity = valid ? all_true : *atLeastOneFalse<octets_number>().as("first,second,third,fourth octet valid");
130
131 return gen::map(
132 gen::tuple(IPv4Octet(octet_validity[0]),
133 IPv4Octet(octet_validity[1]),
134 IPv4Octet(octet_validity[2]),
135 IPv4Octet(octet_validity[3])),
136 [](const auto &octets)
137 {
138 return std::to_string(std::get<0>(octets)) + "." + std::to_string(std::get<1>(octets)) + "."
139 + std::to_string(std::get<2>(octets)) + "." + std::to_string(std::get<3>(octets));
140 });
141}
142
155inline auto asciiPrintableCode() -> Gen<int>
156{
157 static constexpr int ASCII_range_start_code = 32; // ASCII code for space character
158 static constexpr int ASCII_range_end_code = 127; // ASCII code for DEL (not included)
159 static constexpr int ASCII_percent_sign_code = 37; // ASCII code for percent sign
160
161 // Due to IPv6 Scoped Address Architecture (RFC 4007) anything after '%' is not part of IPv6 address but zone_id
162 // Therefore generating '% breaks assumption on validity of generated IPv6 addresses
163 return gen::distinctFrom(gen::inRange(ASCII_range_start_code, ASCII_range_end_code), ASCII_percent_sign_code);
164}
165
181inline auto hexChar(const bool valid = true) -> Gen<std::string>
182{
183 if (valid)
184 {
185 const auto alphapositives = gen::elementOf(std::string("abcdefABCDEF123456789"));
186
187 static constexpr int probability_weight_of_0 = 23;
188 static constexpr int probability_weight_of_alphapositives = 1;
189
190 // "0" should be generated <probability_weight_of_0> times more often
191 return gen::map(gen::weightedOneOf<char>({{probability_weight_of_0, gen::just<char>('0')},
192 {probability_weight_of_alphapositives, alphapositives}}),
193 [](const auto &c)
194 {
195 return std::string{static_cast<char>(c)};
196 });
197 }
198
199 // Generate invalid hexadecimal characters
200 return gen::map(gen::suchThat(asciiPrintableCode(), [](const auto &c)
201 { return isxdigit(c) == 0; }),
202 [](const auto &c)
203 {
204 return std::string{static_cast<char>(c)};
205 });
206}
207
222inline auto IPv6HextetValue(const bool valid = true) -> Gen<std::string>
223{
224 static constexpr int hexchars_number = 4;
225 static constexpr std::array<bool, hexchars_number> all_true = {true, true, true, true};
226 const auto hexchar_validity = valid ? all_true : *atLeastOneFalse<hexchars_number>().as("first,second,third,fourth hexchar in hextet valid");
227
228 return gen::map(
229 gen::tuple(hexChar(hexchar_validity[0]),
230 hexChar(hexchar_validity[1]),
231 hexChar(hexchar_validity[2]),
232 hexChar(hexchar_validity[3])),
233 [](const auto &hexchars)
234 {
235 const auto &[first_hexchar, second_hexchar, third_hexchar, fourth_hexchar] = hexchars;
236 return first_hexchar + second_hexchar + third_hexchar + fourth_hexchar;
237 });
238}
239
247inline std::string removeLeadingZerosFromHextet(const std::string &hextet)
248{
249 const auto first_nonzero = hextet.find_first_not_of('0');
250 return first_nonzero == std::string::npos ? "0" : hextet.substr(first_nonzero);
251}
252
261inline void removeLeadingZerosFromHextets(std::vector<std::string> &hextets)
262{
263 std::transform(hextets.begin(), hextets.end(), hextets.begin(), removeLeadingZerosFromHextet);
264}
265
274inline void replaceSequenceOfZerosWithDoubleColon(std::vector<std::string> &hextets)
275{
276 for (auto longest_zero_sequence = hextets.size(); longest_zero_sequence > 1; --longest_zero_sequence)
277 {
278 auto position = std::search_n(hextets.begin(), hextets.end(), longest_zero_sequence, std::string{"0"});
279 if (position != hextets.end())
280 {
281 const auto it = hextets.insert(position, "::");
282 hextets.erase(it + 1, std::next(it + 1, longest_zero_sequence));
283 return;
284 }
285 }
286}
287
298inline std::string stringifyHextetsToAddressWithColons(const std::vector<std::string> &hextets)
299{
300 std::string result;
301 for (size_t i = 0; i < hextets.size(); ++i)
302 {
303 if (i > 0 && hextets[i] != "::" && hextets[i - 1] != "::")
304 {
305 result += ":";
306 }
307 result += hextets[i];
308 }
309 return result;
310}
311
321inline std::string compressIPv6Address(std::vector<std::string> hextets)
322{
326}
327
345inline auto IPv6Address(const bool valid = true) -> Gen<std::string>
346{
347 static constexpr int number_of_hextets = 8;
348 static constexpr std::array<bool, number_of_hextets> all_true = {true, true, true, true, true, true, true, true};
349 const auto hextet_validity = valid ? all_true : *atLeastOneFalse<number_of_hextets>().as("first,second,third,fourth,fifth,sixth,seventh,eighth hextet valid");
350
351 auto convert_hextets_to_compressed_address = [](std::string first_hextet,
352 std::string second_hextet,
353 std::string third_hextet,
354 std::string fourth_hextet,
355 std::string fifth_hextet,
356 std::string sixth_hextet,
357 std::string seventh_hextet,
358 std::string eighth_hextet)
359 {
360 return compressIPv6Address({std::move(first_hextet),
361 std::move(second_hextet),
362 std::move(third_hextet),
363 std::move(fourth_hextet),
364 std::move(fifth_hextet),
365 std::move(sixth_hextet),
366 std::move(seventh_hextet),
367 std::move(eighth_hextet)});
368 };
369
370 if (valid)
371 {
372 return gen::apply(convert_hextets_to_compressed_address,
373 IPv6HextetValue(hextet_validity[0]),
374 IPv6HextetValue(hextet_validity[1]),
375 IPv6HextetValue(hextet_validity[2]),
376 IPv6HextetValue(hextet_validity[3]),
377 IPv6HextetValue(hextet_validity[4]),
378 IPv6HextetValue(hextet_validity[5]),
379 IPv6HextetValue(hextet_validity[6]),
380 IPv6HextetValue(hextet_validity[7]));
381 }
382 return gen::map(gen::tuple(
383 IPv6HextetValue(hextet_validity[0]),
384 IPv6HextetValue(hextet_validity[1]),
385 IPv6HextetValue(hextet_validity[2]),
386 IPv6HextetValue(hextet_validity[3]),
387 IPv6HextetValue(hextet_validity[4]),
388 IPv6HextetValue(hextet_validity[5]),
389 IPv6HextetValue(hextet_validity[6]),
390 IPv6HextetValue(hextet_validity[7])),
391 [](const auto &hextets)
392 {
393 const auto [first_hextet, second_hextet, third_hextet, fourth_hextet, fifth_hextet, sixth_hextet, seventh_hextet, eighth_hextet] = hextets;
394 return first_hextet + ":" + second_hextet + ":" + third_hextet + ":" + fourth_hextet + ":" + fifth_hextet + ":" + sixth_hextet + ":" + seventh_hextet + ":" + eighth_hextet;
395 });
396}
397
399
405template <>
407{
415 static Gen<RedirectGatewayFlagsValues> arbitrary()
416 {
417 static constexpr int number_of_flags = 9;
418 return gen::map(
419 gen::container<std::vector<int>>(gen::inRange(0, number_of_flags + 1)),
420 [](const auto &bit_positions)
421 {
422 auto flags = static_cast<RedirectGatewayFlagsValues>(0);
423 for (const auto &pos : bit_positions)
424 {
425 flags = static_cast<RedirectGatewayFlagsValues>(flags | (1 << pos));
426 }
427 return flags;
428 });
429 }
430};
431
438template <>
440{
447 static auto arbitrary() -> Gen<openvpn::TunBuilderCapture::RouteBase>
448 {
449 return gen::just(openvpn::TunBuilderCapture::RouteBase{});
450 }
451};
452
461template <>
463{
470 static auto arbitrary() -> Gen<openvpn::TunBuilderCapture::Route>
471 {
472 return gen::just(openvpn::TunBuilderCapture::Route{});
473 }
474};
475
481template <>
483{
490 static auto arbitrary() -> Gen<openvpn::TunBuilderCapture::RouteAddress>
491 {
493 }
494};
495
505
515template <typename T, typename... Ts>
516struct Arbitrary<std::variant<T, Ts...>>
517{
526 static auto arbitrary() -> Gen<std::variant<T, Ts...>>
527 {
528 return gen::oneOf(
529 gen::cast<std::variant<T, Ts...>>(gen::arbitrary<T>()),
530 gen::cast<std::variant<T, Ts...>>(gen::arbitrary<Ts>())...);
531 }
532};
533
534static const std::string ALPHA_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
535static const std::string DIGITS = "1234567890";
536
552inline auto alpha(const bool valid = true) -> Gen<char>
553{
554 if (valid)
555 {
556 return gen::elementOf(std::string_view{ALPHA_CHARACTERS});
557 }
558 return gen::suchThat(gen::character<char>(),
559 [](const char character)
560 { return !std::isalpha(character); });
561}
562
577inline auto from_allowed_chars(const std::string_view &allowed_chars, const bool valid = true) -> Gen<char>
578{
579 if (valid)
580 {
581 return gen::elementOf(allowed_chars);
582 }
583 return gen::suchThat(gen::character<char>(),
584 [allowed_chars](const char character)
585 { return allowed_chars.find(character) == std::string::npos; });
586}
587
605inline auto string_from_allowed_chars(const std::string_view &allowed_chars, const bool valid = true) -> Gen<std::string>
606{
607 if (valid)
608 {
609 return gen::container<std::string>(from_allowed_chars(allowed_chars));
610 }
611 return gen::suchThat(gen::string<std::string>(), [allowed_chars](const auto &string)
612 { return std::any_of(string.begin(), string.end(), [allowed_chars](const auto &character)
613 { return allowed_chars.find(character) == std::string::npos; }); });
614}
615
626inline auto port(const bool valid = true) -> Gen<int>
627{
628 static constexpr int port_upper_bound = 65535;
629 static constexpr int port_lower_bound = 0;
630 if (valid)
631 return gen::inRange(port_lower_bound, port_upper_bound + 1);
632 return gen::suchThat<int>(
633 [](const auto port_number)
634 { return port_number < port_lower_bound || port_number > port_upper_bound; });
635}
636
654inline auto calculateIPPrefixRange(const std::string &ipAddress) -> std::tuple<int, int>
655{
656 std::array<unsigned int, 4> addressParts{};
657 std::istringstream addressStream(ipAddress);
658 std::string part;
659 size_t partIndex = 0;
660
661 while (std::getline(addressStream, part, '.'))
662 {
663 addressParts[partIndex++] = std::stoi(part);
664 }
665
666 const uint32_t addressInteger = (addressParts[0] << 24) | (addressParts[1] << 16) | (addressParts[2] << 8) | addressParts[3];
667
668 auto minimumPrefix = 32;
669 for (auto bitPosition = 0; bitPosition < 32; ++bitPosition)
670 {
671 if (addressInteger & (1U << bitPosition))
672 {
673 minimumPrefix = 32 - bitPosition;
674 break;
675 }
676 }
677
678 return {minimumPrefix, openvpn::IPv4::Addr::SIZE};
679}
680} // namespace rc
681#endif // TEST_GENERATORS_HPP
Route address class that may use non-canonical form.
Definition capture.hpp:295
Base class for route-related functionality representing a network route.
Definition capture.hpp:197
Route class that must use canonical form.
Definition capture.hpp:313
auto calculateIPPrefixRange(const std::string &ipAddress) -> std::tuple< int, int >
Calculates the valid IP prefix range for a given IP address.
auto string_from_allowed_chars(const std::string_view &allowed_chars, const bool valid=true) -> Gen< std::string >
Generates strings based on allowed characters.
auto from_allowed_chars(const std::string_view &allowed_chars, const bool valid=true) -> Gen< char >
Generates characters based on an allowed character set.
std::string stringifyHextetsToAddressWithColons(const std::vector< std::string > &hextets)
Converts a vector of hextets to an IPv6 address string with colons.
auto IPv4Octet(const bool valid=true) -> Gen< int >
Generates a valid or invalid IPv4 octet value.
void replaceSequenceOfZerosWithDoubleColon(std::vector< std::string > &hextets)
Replaces the longest sequence of consecutive "0" strings in a vector with "::".
std::variant< openvpn::TunBuilderCapture::Route, openvpn::TunBuilderCapture::RouteAddress, openvpn::TunBuilderCapture::RouteBase > RouteBased
Alias representing a route-based variant type.
std::string compressIPv6Address(std::vector< std::string > hextets)
Compress an IPv6 address by simplifying its representation.
static const std::string ALPHA_CHARACTERS
std::string removeLeadingZerosFromHextet(const std::string &hextet)
Removes leading zeros from a hextet (IPv6 segment).
static const std::string DIGITS
void removeLeadingZerosFromHextets(std::vector< std::string > &hextets)
Removes leading zeros from a vector of hextets.
auto hexChar(const bool valid=true) -> Gen< std::string >
Generates a valid or invalid hexadecimal character.
auto atLeastOneFalse() -> Gen< std::array< bool, N > >
Generates an array of booleans that contains at least one false.
auto asciiPrintableCode() -> Gen< int >
Generates a random printable ASCII character code.
auto generateValidityFlags(const bool all_valid=true) -> Gen< std::array< bool, N > >
Generates an array of validity flags for component testing.
auto IPv6HextetValue(const bool valid=true) -> Gen< std::string >
Generates a hextet value of an IPv6 address.
auto IPv4Address(const bool valid=true) -> Gen< std::string >
Generates a random IPv4 address.
auto IPv6Address(const bool valid=true) -> Gen< std::string >
Generates a random IPv6 address.
auto alpha(const bool valid=true) -> Gen< char >
Generates alphabetic or non-alphabetic characters.
static Gen< RedirectGatewayFlagsValues > arbitrary()
Generates an arbitrary RedirectGatewayFlagsValues.
static auto arbitrary() -> Gen< openvpn::TunBuilderCapture::RouteAddress >
Generates an arbitrary RouteAddress instance.
static auto arbitrary() -> Gen< openvpn::TunBuilderCapture::RouteBase >
Generates a value of type RouteBase.
static auto arbitrary() -> Gen< openvpn::TunBuilderCapture::Route >
Generates an arbitrary instance of TunBuilderCapture::Route.
static auto arbitrary() -> Gen< std::variant< T, Ts... > >
Generates an arbitrary std::variant containing one of the specified types.
reroute_gw flags
proxy_host_port port