PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_utilMore.cpp
Go to the documentation of this file.
1
8
9#include <gtest/gtest.h>
10
11#include <array>
12#include <chrono>
13#include <cstdint>
14#include <string>
15#include <vector>
16
17#include "profinet/exceptions.h"
18#include "profinet/protocol.h"
19#include "profinet/udpSocket.h"
20#include "profinet/util.h"
21
22using namespace profinet;
23
24namespace
25{
27class UtilMoreTests : public ::testing::Test
28{
29};
30} // namespace
31
33TEST_F(UtilMoreTests, HexFormattersHandleBoundaries)
34{
35 EXPECT_EQ(Hex4(0), "0x0000");
36 EXPECT_EQ(Hex4(0x000F), "0x000F");
37 EXPECT_EQ(Hex4(0xFFFF), "0xFFFF");
38
39 EXPECT_EQ(Hex2(0), "0x00");
40 EXPECT_EQ(Hex2(0x0F), "0x0F");
41 EXPECT_EQ(Hex2(0xFF), "0xFF");
42}
43
45TEST_F(UtilMoreTests, ToVecConvertsStringBytes)
46{
47 const auto bytes = ToVec(std::string{"A\x00\xFF", 3});
48
49 ASSERT_EQ(bytes.size(), 3U);
50 EXPECT_EQ(bytes[0], static_cast<std::uint8_t>('A'));
51 EXPECT_EQ(bytes[1], 0U);
52 EXPECT_EQ(bytes[2], 0xFFU);
53}
54
56TEST_F(UtilMoreTests, ToHexHandlesRawPointerAndEmptyInput)
57{
58 const std::uint8_t bytes[] = {0x00, 0x01, 0xAB, 0xFF};
59
60 EXPECT_EQ(ToHex(bytes, 4), "0001abff");
61 EXPECT_EQ(ToHex(bytes, 0), "");
62 EXPECT_EQ(ToHex(std::vector<std::uint8_t>{}), "");
63}
64
66TEST_F(UtilMoreTests, DecodeBytesPreservesEmbeddedNul)
67{
68 const std::vector<std::uint8_t> bytes{'A', 0, 'B', 0, 0};
69
70 EXPECT_EQ(DecodeBytes(bytes), std::string("A\0B", 3));
71 EXPECT_EQ(DecodeBytes(static_cast<const std::uint8_t*>(nullptr), 0), "");
72}
73
75TEST_F(UtilMoreTests, String2MacAcceptsCanonicalHexDigits)
76{
77 const auto expectedMac = MacAddress{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
78 EXPECT_EQ(String2Mac("AA:bb:CC:dd:EE:ff"), expectedMac);
79}
80
82TEST_F(UtilMoreTests, String2MacRejectsMalformedVariants)
83{
84 EXPECT_THROW(String2Mac("aa-bb-cc-dd-ee-ff"), std::exception);
85 EXPECT_THROW(String2Mac("aa:bb:cc:dd:ee:ff:00"), std::exception);
86 EXPECT_THROW(String2Mac("a:bb:cc:dd:ee:ff"), std::exception);
87 EXPECT_THROW(String2Mac("aa:bb:cc:dd:ee:fg"), std::exception);
88}
89
91TEST_F(UtilMoreTests, IpConversionHandlesBoundaryAddresses)
92{
93 EXPECT_EQ(String2Ip(Ip2String("0.0.0.0").data(), 4), "0.0.0.0");
94 EXPECT_EQ(String2Ip(Ip2String("255.255.255.255").data(), 4), "255.255.255.255");
95}
96
98TEST_F(UtilMoreTests, String2IpRejectsInvalidLength)
99{
100 const std::array<std::uint8_t, 3> shortIp{1, 2, 3};
101 EXPECT_THROW(String2Ip(shortIp.data(), shortIp.size()), std::exception);
102}
103
105TEST_F(UtilMoreTests, Ip2StringRejectsExtraComponents)
106{
107 EXPECT_THROW(Ip2String("1.2.3.4.5"), std::exception);
108}
109
111TEST_F(UtilMoreTests, SkipVlanTagsHandlesTruncatedTag)
112{
113 Bytes frame(14U, 0);
114 frame[12] = 0x81;
115 frame[13] = 0x00;
116
117 EXPECT_EQ(SkipVlanTags(frame), 12U);
118}
119
121TEST_F(UtilMoreTests, SkipVlanTagsLeavesOrdinaryEtherTypeUntouched)
122{
123 Bytes frame(14U, 0);
124 frame[12] = 0x08;
125 frame[13] = 0x00;
126
127 EXPECT_EQ(SkipVlanTags(frame), 12U);
128}
129
131TEST_F(UtilMoreTests, SkipVlanTagsHandlesTripleTaggedFrame)
132{
133 Bytes frame(12U, 0);
134
135 const std::array<std::uint8_t, 4> outer{0x88, 0xA8, 0, 1};
136 const std::array<std::uint8_t, 4> middle{0x81, 0x00, 0, 2};
137 const std::array<std::uint8_t, 4> inner{0x81, 0x00, 0, 3};
138
139 frame.insert(frame.end(), outer.begin(), outer.end());
140 frame.insert(frame.end(), middle.begin(), middle.end());
141 frame.insert(frame.end(), inner.begin(), inner.end());
142 frame.push_back(0x88);
143 frame.push_back(0x92);
144
145 EXPECT_EQ(SkipVlanTags(frame), 24U);
146}
147
149TEST_F(UtilMoreTests, MaxTimeoutRemainingIsPositiveBeforeExpiry)
150{
151 const MaxTimeout timeout(std::chrono::duration<double>(1.0));
152
153 EXPECT_GT(timeout.Remaining().count(), 0.0);
154 EXPECT_FALSE(timeout.TimedOut());
155}
156
158TEST_F(UtilMoreTests, MaxTimeoutRemainingIsZeroAfterExpiry)
159{
160 const MaxTimeout timeout(std::chrono::duration<double>(-1.0));
161
162 EXPECT_TRUE(timeout.TimedOut());
163 EXPECT_EQ(timeout.Remaining().count(), 0.0);
164}
165
167TEST_F(UtilMoreTests, MaxTimeoutHandlesPastDeadline)
168{
169 const MaxTimeout timeout(std::chrono::duration<double>(-0.001));
170
171 EXPECT_TRUE(timeout.TimedOut());
172}
173
175TEST_F(UtilMoreTests, GetExecutableDirectoryReturnsExistingDirectory)
176{
177 const auto directory = GetExecutableDirectory();
178
179 EXPECT_TRUE(directory.is_absolute());
180 EXPECT_TRUE(std::filesystem::exists(directory));
181 EXPECT_TRUE(std::filesystem::is_directory(directory));
182}
183
185TEST_F(UtilMoreTests, GetMacRejectsUnknownInterface)
186{
187 EXPECT_THROW(GetInterfaceMac("this-interface-does-not-exist"), std::exception);
188}
189
191TEST_F(UtilMoreTests, GetMacReadsLoopbackInterface)
192{
193 const auto mac = GetInterfaceMac("lo");
194 EXPECT_EQ(mac.size(), static_cast<std::size_t>(macAddressLength));
195}
196
198TEST_F(UtilMoreTests, SkipVlanTagsRecognizesSupportedTpids)
199{
200 for (const auto tpid : std::array<std::array<std::uint8_t, 2>, 3>{
201 std::array<std::uint8_t, 2>{0x81, 0x00},
202 std::array<std::uint8_t, 2>{0x88, 0xA8},
203 std::array<std::uint8_t, 2>{0x91, 0x00}})
204 {
205 Bytes frame(12U, 0);
206 frame.push_back(tpid[0]);
207 frame.push_back(tpid[1]);
208 frame.push_back(0x00);
209 frame.push_back(0x01);
210
211 EXPECT_EQ(SkipVlanTags(frame), 16U);
212 }
213}
214
216TEST_F(UtilMoreTests, SkipVlanTagsStopsAfterSupportedTag)
217{
218 Bytes frame(12U, 0);
219
220 frame.insert(frame.end(), {0x81, 0x00, 0xC0, 0x00});
221 frame.insert(frame.end(), {0x88, 0x92});
222
223 EXPECT_EQ(SkipVlanTags(frame), 16U);
224}
225
227TEST_F(UtilMoreTests, SkipVlanTagsHandlesShortFrame)
228{
229 EXPECT_EQ(SkipVlanTags(Bytes{}), 12U);
230}
231
233TEST_F(UtilMoreTests, SkipVlanTagsHandlesIncompleteVlanHeader)
234{
235 Bytes frame(15U, 0);
236 frame[12] = 0x81;
237 frame[13] = 0x00;
238 frame[14] = 0xC0;
239
240 EXPECT_EQ(SkipVlanTags(frame), 12U);
241}
243TEST_F(UtilMoreTests, String2MacHandlesBoundaryValues)
244{
245 EXPECT_EQ(
246 String2Mac("00:00:00:00:00:00"),
247 (MacAddress{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
248
249 EXPECT_EQ(
250 String2Mac("FF:FF:FF:FF:FF:FF"),
251 (MacAddress{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
252}
253
255TEST_F(UtilMoreTests, String2MacRejectsWrongDelimiterPosition)
256{
257 EXPECT_THROW(String2Mac("aa:bb-cc:dd:ee:ff"), InvalidMACError);
258}
259
261TEST_F(UtilMoreTests, String2MacRejectsWhitespace)
262{
263 EXPECT_THROW(String2Mac("aa:bb:cc:dd:ee: f"), InvalidMACError);
264}
266TEST_F(UtilMoreTests, Mac2StringRejectsInvalidLength)
267{
268 const std::array<std::uint8_t, 5> shortMac{1, 2, 3, 4, 5};
269
270 EXPECT_THROW(Mac2String(shortMac.data(), shortMac.size()), InvalidMACError);
271}
273TEST_F(UtilMoreTests, String2IpHandlesExactAndExtendedInput)
274{
275 EXPECT_EQ(String2Ip(std::array<std::uint8_t, 4>{192, 168, 1, 42}), "192.168.1.42");
276
277 const std::array<std::uint8_t, 6> extended{192, 168, 1, 42, 0xAA, 0xBB};
278 EXPECT_EQ(String2Ip(extended), "192.168.1.42");
279}
281TEST_F(UtilMoreTests, DecodeBytesPreservesNonPaddedData)
282{
283 const std::vector<std::uint8_t> data{'a', 'b', 'c'};
284
285 EXPECT_EQ(DecodeBytes(data), "abc");
286}
287
289TEST_F(UtilMoreTests, DecodeBytesHandlesAllNuls)
290{
291 const std::vector<std::uint8_t> data(8U, 0);
292
293 EXPECT_EQ(DecodeBytes(data), "");
294}
Invalid MAC address format.
Definition exceptions.h:639
Time-limited-operation helper.
Definition util.h:267
bool TimedOut() const
Whether the countdown has elapsed.
Definition util.h:279
std::chrono::duration< double > Remaining() const
Time left before the countdown elapses.
Definition util.h:286
Declares exceptions and error types used by the PROFINET IO controller stack.
std::string Hex2(std::uint8_t value)
Format an 8-bit unsigned integer as a 2-digit hexadecimal string.
Definition util.cpp:49
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
MacAddress GetInterfaceMac(const std::string &interface)
Get the MAC address assigned to a network interface.
std::vector< std::uint8_t > ToVec(const std::string &input)
Convert a string into a vector of raw bytes.
Definition util.cpp:54
std::string String2Ip(std::span< const std::uint8_t > ipBytes)
Format raw IPv4 address bytes as a dotted string.
Definition util.cpp:206
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
std::array< std::uint8_t, 4 > Ip2String(const std::string &ipStr)
Parse a dotted-decimal IPv4 address string.
Definition util.cpp:222
std::filesystem::path GetExecutableDirectory()
Retrieves the absolute directory path of the currently running executable.
MacAddress String2Mac(const std::string &macStr)
Parse a colon-separated MAC address string.
Definition util.cpp:122
std::size_t SkipVlanTags(std::span< const std::uint8_t > frame) noexcept
Return the byte offset of the real EtherType in a raw Ethernet frame.
Definition util.cpp:70
constexpr int macAddressLength
Constant lenght of a mac address.
Definition util.h:37
std::string ToHex(const std::uint8_t *data, std::size_t len)
Hex-encode a byte buffer.
Definition util.cpp:104
std::string Hex4(std::uint16_t value)
Format a 16-bit unsigned integer as a 4-digit hexadecimal string.
Definition util.cpp:44
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
std::string Mac2String(const MacAddress &mac)
Format a MAC address as a colon-separated string.
Definition util.cpp:188
std::string DecodeBytes(const std::uint8_t *data, std::size_t len)
Decode bytes to a UTF-8 string, stripping trailing NUL bytes.
Definition util.cpp:246
PROFINET protocol wire-format structures and serialization helpers.
Bytes frame
Declares the portable IPv4 UDP socket implementation.
Declares Linux Ethernet, addressing, timing, and utility helpers used by the PROFINET stack.