PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_in_memory_ethernetMore.cpp
Go to the documentation of this file.
1
8
9#include <gtest/gtest.h>
10
11#include <chrono>
12#include <cstdint>
13#include <memory>
14#include <thread>
15#include <vector>
16
17#include "profinet/protocol.h"
19
20namespace profinet::sim
21{
22
23namespace
24{
25
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};
29
35Bytes MakeFrame(const MacAddress& destination,
36 const MacAddress& source,
37 const std::uint8_t marker)
38{
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);
45 return frame;
46}
47
48} // namespace
49
51TEST(InMemoryEthernetRoutingTests, UnicastReachesOnlyMatchingEndpoint)
52{
54 auto socketA = bus.CreateSocket(kMacA);
55 auto socketB = bus.CreateSocket(kMacB);
56 auto socketC = bus.CreateSocket(kMacC);
57 socketA->SetTimeout(std::chrono::milliseconds(10));
58 socketB->SetTimeout(std::chrono::milliseconds(10));
59 socketC->SetTimeout(std::chrono::milliseconds(10));
60
61 const Bytes frame = MakeFrame(kMacB, kMacA, 0x42U);
62 socketA->Send(frame);
63
64 EXPECT_EQ(socketB->Recv(), frame);
65 EXPECT_TRUE(socketC->Recv().empty());
66 EXPECT_TRUE(socketA->Recv().empty());
67}
68
70TEST(InMemoryEthernetRoutingTests, BroadcastIsFloodedWithoutLoopback)
71{
73 auto socketA = bus.CreateSocket(kMacA);
74 auto socketB = bus.CreateSocket(kMacB);
75 auto socketC = bus.CreateSocket(kMacC);
76 for (auto* socket : {socketA.get(), socketB.get(), socketC.get()})
77 {
78 socket->SetTimeout(std::chrono::milliseconds(10));
79 }
80
81 const MacAddress broadcast{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
82 const Bytes frame = MakeFrame(broadcast, kMacA, 0x43U);
83 socketA->Send(frame);
84
85 EXPECT_EQ(socketB->Recv(), frame);
86 EXPECT_EQ(socketC->Recv(), frame);
87 EXPECT_TRUE(socketA->Recv().empty());
88}
89
91TEST(InMemoryEthernetRoutingTests, MulticastIsFlooded)
92{
94 auto socketA = bus.CreateSocket(kMacA);
95 auto socketB = bus.CreateSocket(kMacB);
96 auto socketC = bus.CreateSocket(kMacC);
97 for (auto* socket : {socketA.get(), socketB.get(), socketC.get()})
98 {
99 socket->SetTimeout(std::chrono::milliseconds(10));
100 }
101
102 const MacAddress multicast{0x01, 0x0E, 0xCF, 0x00, 0x00, 0x00};
103 const Bytes frame = MakeFrame(multicast, kMacA, 0x44U);
104 socketA->Send(frame);
105
106 EXPECT_EQ(socketB->Recv(), frame);
107 EXPECT_EQ(socketC->Recv(), frame);
108}
109
111TEST(InMemoryEthernetRoutingTests, SameMacSocketsDoNotLoopBackTraffic)
112{
114 auto tx = bus.CreateSocket(kMacA);
115 auto rx = bus.CreateSocket(kMacA);
116 auto peer = bus.CreateSocket(kMacB);
117 tx->SetTimeout(std::chrono::milliseconds(10));
118 rx->SetTimeout(std::chrono::milliseconds(10));
119 peer->SetTimeout(std::chrono::milliseconds(10));
120
121 const Bytes frame = MakeFrame(kMacB, kMacA, 0x45U);
122 tx->Send(frame);
123
124 EXPECT_EQ(peer->Recv(), frame);
125 EXPECT_TRUE(rx->Recv().empty());
126}
127
129TEST(InMemoryEthernetSocketTests, ReceiveTimeoutReturnsEmptyFrame)
130{
132 auto socket = bus.CreateSocket(kMacA);
133 socket->SetTimeout(std::chrono::milliseconds(2));
134
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;
138
139 EXPECT_GE(elapsed, std::chrono::milliseconds(1));
140}
141
143TEST(InMemoryEthernetSocketTests, NegativeTimeoutThrows)
144{
146 auto socket = bus.CreateSocket(kMacA);
147
148 EXPECT_THROW(socket->SetTimeout(std::chrono::milliseconds(-1)), std::invalid_argument);
149}
150
152TEST(InMemoryEthernetRoutingTests, ShortFrameThrows)
153{
155 auto socket = bus.CreateSocket(kMacA);
156
157 EXPECT_THROW(socket->Send(Bytes{0x00, 0x01, 0x02}), std::invalid_argument);
158}
159
161TEST(InMemoryEthernetBusTests, ClearQueuesRemovesPendingFrames)
162{
164 auto socketA = bus.CreateSocket(kMacA);
165 auto socketB = bus.CreateSocket(kMacB);
166 socketB->SetTimeout(std::chrono::milliseconds(10));
167
168 socketA->Send(MakeFrame(kMacB, kMacA, 0x46U));
169 EXPECT_EQ(bus.EndpointCount(), 2U);
170
171 bus.ClearQueues();
172 EXPECT_TRUE(socketB->Recv().empty());
173 EXPECT_EQ(bus.EndpointCount(), 2U);
174}
175
177TEST(InMemoryEthernetBusTests, EndpointDestructionDetachesSocket)
178{
180 auto socketA = bus.CreateSocket(kMacA);
181 EXPECT_EQ(bus.EndpointCount(), 1U);
182
183 {
184 auto socketB = bus.CreateSocket(kMacB);
185 EXPECT_EQ(bus.EndpointCount(), 2U);
186 }
187
188 EXPECT_EQ(bus.EndpointCount(), 1U);
189 socketA.reset();
190 EXPECT_EQ(bus.EndpointCount(), 0U);
191}
192
193} // namespace profinet::sim
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.
Definition util.h:48
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
PROFINET protocol wire-format structures and serialization helpers.
std::unique_ptr< MockRawEthernetSocket > tx
std::unique_ptr< MockRawEthernetSocket > rx
Bytes frame