9#include <gtest/gtest.h>
26constexpr MacAddress kMacA{0x02, 0x00, 0x00, 0x00, 0x00, 0x0A};
27constexpr MacAddress kMacB{0x02, 0x00, 0x00, 0x00, 0x00, 0x0B};
28constexpr MacAddress kMacC{0x02, 0x00, 0x00, 0x00, 0x00, 0x0C};
37 const std::uint8_t marker)
40 frame.insert(
frame.end(), destination.begin(), destination.end());
41 frame.insert(
frame.end(), source.begin(), source.end());
42 frame.push_back(0x88U);
43 frame.push_back(0x92U);
44 frame.push_back(marker);
51TEST(InMemoryEthernetRoutingTests, UnicastReachesOnlyMatchingEndpoint)
57 socketA->SetTimeout(std::chrono::milliseconds(10));
58 socketB->SetTimeout(std::chrono::milliseconds(10));
59 socketC->SetTimeout(std::chrono::milliseconds(10));
61 const Bytes frame = MakeFrame(kMacB, kMacA, 0x42U);
64 EXPECT_EQ(socketB->Recv(),
frame);
65 EXPECT_TRUE(socketC->Recv().empty());
66 EXPECT_TRUE(socketA->Recv().empty());
70TEST(InMemoryEthernetRoutingTests, BroadcastIsFloodedWithoutLoopback)
76 for (
auto* socket : {socketA.get(), socketB.get(), socketC.get()})
78 socket->SetTimeout(std::chrono::milliseconds(10));
81 const MacAddress broadcast{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
82 const Bytes frame = MakeFrame(broadcast, kMacA, 0x43U);
85 EXPECT_EQ(socketB->Recv(),
frame);
86 EXPECT_EQ(socketC->Recv(),
frame);
87 EXPECT_TRUE(socketA->Recv().empty());
91TEST(InMemoryEthernetRoutingTests, MulticastIsFlooded)
97 for (
auto* socket : {socketA.get(), socketB.get(), socketC.get()})
99 socket->SetTimeout(std::chrono::milliseconds(10));
102 const MacAddress multicast{0x01, 0x0E, 0xCF, 0x00, 0x00, 0x00};
103 const Bytes frame = MakeFrame(multicast, kMacA, 0x44U);
104 socketA->Send(
frame);
106 EXPECT_EQ(socketB->Recv(),
frame);
107 EXPECT_EQ(socketC->Recv(),
frame);
111TEST(InMemoryEthernetRoutingTests, SameMacSocketsDoNotLoopBackTraffic)
117 tx->SetTimeout(std::chrono::milliseconds(10));
118 rx->SetTimeout(std::chrono::milliseconds(10));
119 peer->SetTimeout(std::chrono::milliseconds(10));
121 const Bytes frame = MakeFrame(kMacB, kMacA, 0x45U);
124 EXPECT_EQ(peer->Recv(),
frame);
125 EXPECT_TRUE(
rx->Recv().empty());
129TEST(InMemoryEthernetSocketTests, ReceiveTimeoutReturnsEmptyFrame)
133 socket->SetTimeout(std::chrono::milliseconds(2));
135 const auto start = std::chrono::steady_clock::now();
136 EXPECT_TRUE(socket->Recv().empty());
137 const auto elapsed = std::chrono::steady_clock::now() - start;
139 EXPECT_GE(elapsed, std::chrono::milliseconds(1));
143TEST(InMemoryEthernetSocketTests, NegativeTimeoutThrows)
148 EXPECT_THROW(socket->SetTimeout(std::chrono::milliseconds(-1)), std::invalid_argument);
152TEST(InMemoryEthernetRoutingTests, ShortFrameThrows)
157 EXPECT_THROW(socket->Send(
Bytes{0x00, 0x01, 0x02}), std::invalid_argument);
161TEST(InMemoryEthernetBusTests, ClearQueuesRemovesPendingFrames)
166 socketB->SetTimeout(std::chrono::milliseconds(10));
168 socketA->Send(MakeFrame(kMacB, kMacA, 0x46U));
172 EXPECT_TRUE(socketB->Recv().empty());
177TEST(InMemoryEthernetBusTests, EndpointDestructionDetachesSocket)
Small virtual Ethernet switch suitable for deterministic tests and demos.
std::size_t EndpointCount() const
Return the number of currently connected socket endpoints.
std::unique_ptr< InMemoryEthernetSocket > CreateSocket(MacAddress localMac)
Create one virtual raw-Ethernet endpoint.
void ClearQueues()
Remove all queued frames without disconnecting endpoints.
Thread-safe in-memory Layer-2 Ethernet switch for PROFINET simulation.
TEST(InMemoryEthernetRoutingTests, UnicastReachesOnlyMatchingEndpoint)
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
PROFINET protocol wire-format structures and serialization helpers.
std::unique_ptr< MockRawEthernetSocket > tx
std::unique_ptr< MockRawEthernetSocket > rx