Public Access
The plan and decisions Q105 to Q116 (docs/milestones/S1.md). lib/net: strict IPv4 parsing, prefix and mask, and the checks a Fixed setting must pass, each refusal with its reason. A Saved Network is Automatic or Fixed (address/prefix and an optional gateway), kept with it in flash. Settings: two DNS servers (9.9.9.9, 1.1.1.1), "Always use my DNS", two NTP servers (pool.ntp.org, time.cloudflare.com). Host-tested: 383 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
102 lines
4.8 KiB
C++
102 lines
4.8 KiB
C++
#include <unity.h>
|
|
|
|
#include <string>
|
|
|
|
#include "ipv4.h"
|
|
|
|
using namespace roro::net;
|
|
|
|
void setUp() {}
|
|
void tearDown() {}
|
|
|
|
static uint32_t ip(const char* s) {
|
|
uint32_t a = 0;
|
|
TEST_ASSERT_TRUE_MESSAGE(parseIpv4(s, a), s);
|
|
return a;
|
|
}
|
|
|
|
void test_parse_and_format() {
|
|
TEST_ASSERT_EQUAL_HEX32(0x0A27270C, ip("10.39.39.12"));
|
|
TEST_ASSERT_EQUAL_HEX32(0x00000000, ip("0.0.0.0"));
|
|
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, ip("255.255.255.255"));
|
|
TEST_ASSERT_EQUAL_STRING("10.39.39.12", formatIpv4(0x0A27270C).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("192.168.1.254", formatIpv4(ip("192.168.1.254")).c_str());
|
|
}
|
|
|
|
void test_parse_refuses() {
|
|
uint32_t a;
|
|
for (const char* bad : {"", "10.39.39", "10.39.39.12.1", "10.39.39.256", "10..39.12", "10.39.39.", ".10.39.39",
|
|
"10.39.39.1a", " 10.39.39.12", "10.39.39.12 ", "1.2.3.-4", "01234.1.1.1", "1.2.3.4/24"})
|
|
TEST_ASSERT_FALSE_MESSAGE(parseIpv4(bad, a), bad);
|
|
}
|
|
|
|
void test_prefix_and_mask() {
|
|
TEST_ASSERT_EQUAL_HEX32(0xFFFFFF00, maskOf(24));
|
|
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFC, maskOf(30));
|
|
TEST_ASSERT_EQUAL_HEX32(0x80000000, maskOf(1));
|
|
TEST_ASSERT_EQUAL_HEX32(0xFFFF0000, maskOf(16));
|
|
TEST_ASSERT_EQUAL_STRING("255.255.255.0", formatIpv4(maskOf(24)).c_str());
|
|
}
|
|
|
|
// Q111: what a Fixed setting must satisfy, and the reason given when it doesn't.
|
|
void test_fixed_setting_accepted() {
|
|
TEST_ASSERT_EQUAL_STRING("", checkFixed({ip("10.39.39.13"), 24, ip("10.39.39.1")}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("", checkFixed({ip("192.168.4.2"), 30, ip("192.168.4.1")}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("", checkFixed({ip("10.0.0.5"), 8, 0}).c_str()); // Q107: no gateway is fine
|
|
}
|
|
|
|
void test_fixed_setting_refused() {
|
|
TEST_ASSERT_EQUAL_STRING("The prefix must be 1 to 30", checkFixed({ip("10.39.39.13"), 0, 0}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The prefix must be 1 to 30", checkFixed({ip("10.39.39.13"), 31, 0}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("10.39.39.0 is the network's own address", checkFixed({ip("10.39.39.0"), 24, 0}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("10.39.39.255 is the broadcast address", checkFixed({ip("10.39.39.255"), 24, 0}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The gateway 10.39.40.1 isn't in 10.39.39.0/24",
|
|
checkFixed({ip("10.39.39.13"), 24, ip("10.39.40.1")}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The gateway can't be this device's address",
|
|
checkFixed({ip("10.39.39.13"), 24, ip("10.39.39.13")}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The gateway 10.39.39.255 is the broadcast address",
|
|
checkFixed({ip("10.39.39.13"), 24, ip("10.39.39.255")}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("0.0.0.0 isn't an address a device can have", checkFixed({0, 24, 0}).c_str());
|
|
}
|
|
|
|
// How a Fixed setting is typed on the console and kept in flash: "address/prefix [gateway]".
|
|
void test_fixed_setting_as_text() {
|
|
FixedIp f;
|
|
TEST_ASSERT_EQUAL_STRING("", parseFixed("10.39.39.13/24 10.39.39.1", f).c_str());
|
|
TEST_ASSERT_EQUAL_HEX32(ip("10.39.39.13"), f.address);
|
|
TEST_ASSERT_EQUAL_UINT8(24, f.prefix);
|
|
TEST_ASSERT_EQUAL_HEX32(ip("10.39.39.1"), f.gateway);
|
|
TEST_ASSERT_EQUAL_STRING("10.39.39.13/24 10.39.39.1", formatFixed(f).c_str());
|
|
|
|
TEST_ASSERT_EQUAL_STRING("", parseFixed("192.168.4.2/30", f).c_str());
|
|
TEST_ASSERT_EQUAL_HEX32(0, f.gateway);
|
|
TEST_ASSERT_EQUAL_STRING("192.168.4.2/30", formatFixed(f).c_str());
|
|
|
|
TEST_ASSERT_EQUAL_STRING("Write it as address/prefix, then the gateway if there is one", parseFixed("10.39.39.13", f).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("10.39.39.300 isn't an IPv4 address", parseFixed("10.39.39.300/24", f).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The prefix must be 1 to 30", parseFixed("10.39.39.13/abc", f).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("gw isn't an IPv4 address", parseFixed("10.39.39.13/24 gw", f).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("The gateway 10.39.40.1 isn't in 10.39.39.0/24", parseFixed("10.39.39.13/24 10.39.40.1", f).c_str());
|
|
}
|
|
|
|
// An NTP server is an address or a host name (Q110).
|
|
void test_host_names() {
|
|
for (const char* good : {"pool.ntp.org", "time.cloudflare.com", "10.39.39.1", "ntp", "a-b.example", "0.be.pool.ntp.org"})
|
|
TEST_ASSERT_TRUE_MESSAGE(validHost(good), good);
|
|
for (const char* bad : {"", "pool .ntp.org", "-ntp.org", "ntp-.org", "ntp..org", ".ntp.org", "ntp.org.", "n_tp.org", "häst.se",
|
|
"999.1.1.1", "1.2.3"})
|
|
TEST_ASSERT_FALSE_MESSAGE(validHost(bad), bad);
|
|
}
|
|
|
|
int main() {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_parse_and_format);
|
|
RUN_TEST(test_parse_refuses);
|
|
RUN_TEST(test_prefix_and_mask);
|
|
RUN_TEST(test_fixed_setting_accepted);
|
|
RUN_TEST(test_fixed_setting_refused);
|
|
RUN_TEST(test_fixed_setting_as_text);
|
|
RUN_TEST(test_host_names);
|
|
return UNITY_END();
|
|
}
|