OXYGENHUB
◆ main openmsx / src / unittest / UnapiNetWire_test.cc
4.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "catch.hpp"
2
3
#include "UnapiNetWire.hh"
4
5
#include <array>
6
#include <cstdint>
7
#include <span>
8
#include <vector>
9
10
using namespace openmsx;
11
12
// Compile-time guards: the concept accepts our wire structs and rejects unsafe
13
// types (padding, containers, floats).
14
static_assert(wire_layout<DetectResult>);
15
static_assert(wire_layout<TcpStateResult>);
16
static_assert(wire_layout<TcpOpenParams>);
17
static_assert(wire_layout<UdpRecvResultHeader>);
18
static_assert(wire_layout<IcmpRecvResult>);
19
namespace {
20
struct Padded { uint8_t a; uint32_t b; }; // has padding -> not unique object rep
21
}
22
static_assert(!wire_layout<Padded>);
23
static_assert(!wire_layout<float>);
24
static_assert(!wire_layout<std::vector<uint8_t>>);
25
26
[[nodiscard]] static std::vector<uint8_t> vec(std::span<const uint8_t> s)
27
{
28
	return {s.begin(), s.end()};
29
}
30
31
TEST_CASE("UnapiNetWire: default-constructed structs are valid success frames")
32
{
33
	// v2 rule 1 baked into the types: '{}' yields status 0x00 - and for
34
	// DETECT the full acceptance triplet 00 55 02.
35
	CHECK(vec(toBytes(DetectResult{})) ==
36
	      std::vector<uint8_t>{0x00, 0x55, 0x02, 0x00, 0x00});
37
	CHECK(toBytes(TcpStateResult{})[0]      == 0x00);
38
	CHECK(toBytes(TcpRecvResultHeader{})[0] == 0x00);
39
	CHECK(toBytes(UdpRecvResultHeader{})[0] == 0x00);
40
	CHECK(toBytes(IcmpRecvResult{})[0]      == 0x00);
41
	CHECK(toBytes(OpenResult{})[0]          == 0x00);
42
	CHECK(toBytes(GetLocalIpResult{})[0]    == 0x00);
43
	CHECK(toBytes(UdpStateResult{})[0]      == 0x00);
44
}
45
46
TEST_CASE("UnapiNetWire: DETECT result is byte-identical")
47
{
48
	DetectResult r{};
49
	r.caps = 0x1F; // DNS + TCP active/passive + UDP + ICMP
50
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x55, 0x02, 0x1F, 0x00});
51
}
52
53
TEST_CASE("UnapiNetWire: TCP_STATE result is byte-identical (mixed endian)")
54
{
55
	TcpStateResult r{};
56
	r.state       = 0x04;        // TCP_ESTABLISHED
57
	r.avail       = 0x0102;
58
	r.closeReason = 0x01;
59
	r.remoteIp    = 0xC0A80101;  // 192.168.1.1
60
	r.remotePort  = 0x1F90;      // 8080
61
	r.localPort   = 0x0050;      // 80
62
63
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{
64
		0x00, 0x04, 0x02, 0x01, 0x01, 0xC0, 0xA8, 0x01, 0x01, 0x90, 0x1F, 0x50, 0x00});
65
}
66
67
TEST_CASE("UnapiNetWire: OPEN result is status + 1-based handle")
68
{
69
	OpenResult r{};
70
	r.handle = 0x03;
71
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x03});
72
}
73
74
TEST_CASE("UnapiNetWire: TCP_RECV header is status + little-endian length")
75
{
76
	TcpRecvResultHeader r{};
77
	r.actualLen = 0x0102;
78
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x02, 0x01});
79
}
80
81
TEST_CASE("UnapiNetWire: GET_LOCALIP result is status + 4-byte big-endian")
82
{
83
	GetLocalIpResult r{};
84
	r.ip = 0xC0A80101;
85
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0xC0, 0xA8, 0x01, 0x01});
86
}
87
88
TEST_CASE("UnapiNetWire: UDP_STATE result is status + little-endian size")
89
{
90
	UdpStateResult r{};
91
	r.firstDgramSize = 0x0004;
92
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x04, 0x00});
93
}
94
95
TEST_CASE("UnapiNetWire: DNS_QUERY fast path = status + marker + big-endian IP")
96
{
97
	DnsQueryResult r{};
98
	r.ip = 0xC0A80101;
99
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x01, 0xC0, 0xA8, 0x01, 0x01});
100
}
101
102
TEST_CASE("UnapiNetWire: DNS_STATUS complete = status + state + big-endian IP")
103
{
104
	DnsStatusResult r{};
105
	r.ip = 0x08080808;           // 8.8.8.8
106
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0x02, 0x08, 0x08, 0x08, 0x08});
107
}
108
109
TEST_CASE("UnapiNetWire: DNS_STATUS failed lookup = success carrying bad news")
110
{
111
	DnsStatusFailed r{};
112
	r.sub = 0x03;                // "no such host"
113
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{0x00, 0xFF, 0x03});
114
}
115
116
TEST_CASE("UnapiNetWire: UDP_RECV header (status, IP big, port/len little)")
117
{
118
	UdpRecvResultHeader r{};
119
	r.srcIp     = 0xC0A80101;
120
	r.srcPort   = 0x0035;        // 53
121
	r.actualLen = 0x0004;
122
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{
123
		0x00, 0xC0, 0xA8, 0x01, 0x01, 0x35, 0x00, 0x04, 0x00});
124
}
125
126
TEST_CASE("UnapiNetWire: ICMP_RECV record (12 bytes, status replaces hasData)")
127
{
128
	IcmpRecvResult r{};
129
	r.srcIp      = 0x08080808;
130
	r.ttl        = 0x40;
131
	r.identifier = 0x1234;
132
	r.sequence   = 0x0001;
133
	r.dataLen    = 0x0020;
134
	CHECK(vec(toBytes(r)) == std::vector<uint8_t>{
135
		0x00, 0x08, 0x08, 0x08, 0x08, 0x40, 0x34, 0x12, 0x01, 0x00, 0x20, 0x00});
136
}
137
138
TEST_CASE("UnapiNetWire: TCP_OPEN params round-trip (IP big, ports little)")
139
{
140
	std::array<uint8_t, 11> bytes{
141
		0xC0, 0xA8, 0x01, 0x01, 0x90, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x02};
142
	auto p = fromBytes<TcpOpenParams>(bytes);
143
	CHECK(uint32_t(p.remoteIp)   == 0xC0A80101);
144
	CHECK(uint16_t(p.remotePort) == 0x1F90);
145
	CHECK(uint16_t(p.localPort)  == 0x0000);
146
	CHECK(uint16_t(p.timeout)    == 0x0000);
147
	CHECK(p.flags                == 0x02);
148
}
149
150
TEST_CASE("UnapiNetWire: UDP_SEND param header round-trip")
151
{
152
	std::array<uint8_t, 9> bytes{
153
		0x01, 0xC0, 0xA8, 0x01, 0xFF, 0x99, 0x1F, 0x03, 0x00};
154
	auto p = fromBytes<UdpSendParamHeader>(bytes);
155
	CHECK(p.handle             == 0x01);
156
	CHECK(uint32_t(p.destIp)   == 0xC0A801FF);
157
	CHECK(uint16_t(p.destPort) == 0x1F99); // 8089
158
	CHECK(uint16_t(p.len)      == 0x0003);
159
}