• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-serial / 6

13 Jan 2026 06:31AM UTC coverage: 76.817% (+4.0%) from 72.836%
6

Pull #1049

github

8b2ffc
Ilia1S
Remove fw from groups
Pull Request #1049: WB-MR templates: Add delays

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

830.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.35
/test/modbus_ext_common_test.cpp
1
#include "crc16.h"
2
#include "modbus_ext_common.h"
3
#include "serial_exc.h"
4
#include "gtest/gtest.h"
5

6
#include <cmath>
7
#include <list>
8

9
namespace
10
{
11
    void SetCrc(std::vector<uint8_t>& data)
11✔
12
    {
13
        if (data.size() < 2) {
11✔
14
            return;
×
15
        }
16
        auto crc = CRC16::CalculateCRC16(data.data(), data.size() - 2);
11✔
17
        data[data.size() - 1] = crc & 0xFF;
11✔
18
        data[data.size() - 2] = (crc >> 8) & 0xFF;
11✔
19
    }
20

21
    class TPortMock: public TPort
22
    {
23
    public:
24
        std::vector<std::vector<uint8_t>> Requests;
25
        std::list<std::vector<uint8_t>> Responses;
26
        bool ThrowTimeout = false;
27

28
        TPortMock()
16✔
29
        {}
16✔
30

31
        void Open() override
×
32
        {}
33
        void Close() override
×
34
        {}
35
        bool IsOpen() const override
×
36
        {
37
            return false;
×
38
        }
39
        void CheckPortOpen() const override
×
40
        {}
41

42
        void WriteBytes(const uint8_t* buf, int count) override
23✔
43
        {
44
            std::vector<uint8_t> data(buf, buf + count);
46✔
45
            Requests.emplace_back(std::move(data));
23✔
46
        }
23✔
47

48
        uint8_t ReadByte(const std::chrono::microseconds& timeout) override
×
49
        {
50
            return 0;
×
51
        }
52

53
        TReadFrameResult ReadFrame(uint8_t* buf,
23✔
54
                                   size_t count,
55
                                   const std::chrono::microseconds& responseTimeout,
56
                                   const std::chrono::microseconds& frameTimeout,
57
                                   TFrameCompletePred frame_complete = 0) override
58
        {
59
            if (ThrowTimeout) {
23✔
60
                throw TResponseTimeoutException();
1✔
61
            }
62
            if (Responses.empty()) {
22✔
63
                throw TResponseTimeoutException();
×
64
            }
65
            if (Responses.front().size() > count) {
22✔
66
                throw std::runtime_error("Buffer is too small");
×
67
            }
68
            TReadFrameResult res;
22✔
69
            res.Count = Responses.front().size();
22✔
70
            memcpy(buf, Responses.front().data(), Responses.front().size());
22✔
71
            Responses.pop_front();
22✔
72
            return res;
22✔
73
        }
74

75
        void SkipNoise() override
×
76
        {}
77

78
        void SleepSinceLastInteraction(const std::chrono::microseconds& us) override
16✔
79
        {}
16✔
80

81
        std::string GetDescription(bool verbose) const override
1✔
82
        {
83
            return std::string();
1✔
84
        }
85

86
        std::chrono::microseconds GetSendTimeBytes(double bytesNumber) const override
40✔
87
        {
88
            // 8-N-2
89
            auto bits = std::ceil(11 * bytesNumber);
40✔
90
            return GetSendTimeBits(bits);
40✔
91
        }
92

93
        std::chrono::microseconds GetSendTimeBits(size_t bitsNumber) const override
76✔
94
        {
95
            // 115200 bps
96
            auto us = std::ceil((1000000.0 * bitsNumber) / 115200.0);
76✔
97
            return std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(us));
76✔
98
        }
99

100
        void AddResponse(const std::vector<uint8_t>& data)
22✔
101
        {
102
            Responses.push_back(data);
22✔
103
            if (Responses.back().size() >= 2 && Responses.back()[Responses.back().size() - 2] == 0 &&
33✔
104
                Responses.back()[Responses.back().size() - 1] == 0)
11✔
105
            {
106
                SetCrc(Responses.back());
11✔
107
            }
108
        }
22✔
109
    };
110

111
    class TTestEventsVisitor: public ModbusExt::IEventsVisitor
112
    {
113
    public:
114
        std::unordered_map<uint16_t, uint16_t> Events;
115
        bool Reboot = false;
116

117
        virtual void Event(uint8_t slaveId,
2✔
118
                           uint8_t eventType,
119
                           uint16_t eventId,
120
                           const uint8_t* data,
121
                           size_t dataSize) override
122
        {
123
            if (eventType == ModbusExt::TEventType::REBOOT) {
2✔
124
                Reboot = true;
1✔
125
                return;
1✔
126
            }
127
            Events[eventId] = data[0] | (data[1] << 8);
1✔
128
        }
129
    };
130

131
} // namespace
132

133
TEST(TModbusExtTest, EventsEnablerOneReg)
8✔
134
{
135
    TPortMock port;
4✔
136
    port.AddResponse({0x0A, 0x46, 0x18, 0x01, 0x01, 0xE9, 0x1E});
2✔
137

138
    std::map<uint16_t, bool> response;
4✔
139
    Modbus::TModbusRTUTraits traits;
4✔
140
    ModbusExt::TEventsEnabler ev(10, port, traits, [&response](uint8_t type, uint16_t reg, bool enabled) {
1✔
141
        response[reg] = enabled;
1✔
142
    });
5✔
143
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
144

145
    EXPECT_NO_THROW(ev.SendRequests());
2✔
146

147
    EXPECT_EQ(port.Requests.size(), 1);
2✔
148

149
    auto request = port.Requests.front();
4✔
150

151
    EXPECT_EQ(request.size(), 11);
2✔
152
    EXPECT_EQ(request[0], 0x0A); // slave id
2✔
153
    EXPECT_EQ(request[1], 0x46); // command
2✔
154
    EXPECT_EQ(request[2], 0x18); // subcommand
2✔
155
    EXPECT_EQ(request[3], 0x05); // settings size
2✔
156

157
    EXPECT_EQ(request[4], 0x01); // event type
2✔
158
    EXPECT_EQ(request[5], 0x00); // address MSB
2✔
159
    EXPECT_EQ(request[6], 0x65); // address LSB
2✔
160
    EXPECT_EQ(request[7], 0x01); // count
2✔
161
    EXPECT_EQ(request[8], 0x02); // priority
2✔
162

163
    EXPECT_EQ(request[9], 0xC5);  // CRC16 LSB
2✔
164
    EXPECT_EQ(request[10], 0x90); // CRC16 MSB
2✔
165

166
    EXPECT_EQ(response.size(), 1);
2✔
167
    EXPECT_TRUE(response[101]);
2✔
168
}
2✔
169

170
TEST(TModbusExtTest, EventsEnablerIllegalFunction)
8✔
171
{
172
    TPortMock port;
4✔
173
    port.AddResponse({0x0A, 0xC6, 0x01, 0xC3, 0xA2});
2✔
174

175
    Modbus::TModbusRTUTraits traits;
4✔
176
    ModbusExt::TEventsEnabler ev(10, port, traits, [](uint8_t, uint16_t, bool) {});
4✔
177
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
178

179
    EXPECT_THROW(ev.SendRequests(), Modbus::TModbusExceptionError);
2✔
180
}
2✔
181

182
TEST(TModbusExtTest, EventsEnablerTwoRanges)
8✔
183
{
184
    TPortMock port;
4✔
185
    // 0x03 = 0b00000011
186
    // 0xFB = 0b11111011
187
    // 0x02 = 0b00000010
188
    port.AddResponse({0x0A, 0x46, 0x18, 0x03, 0x03, 0xFB, 0x02, 0xAD, 0x11});
2✔
189

190
    std::map<uint16_t, bool> response;
4✔
191
    Modbus::TModbusRTUTraits traits;
4✔
192
    ModbusExt::TEventsEnabler ev(10, port, traits, [&response](uint8_t type, uint16_t reg, bool enabled) {
12✔
193
        response[reg] = enabled;
12✔
194
    });
16✔
195

196
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
197
    ev.AddRegister(102, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
198

199
    ev.AddRegister(103, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
200
    ev.AddRegister(104, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
201
    ev.AddRegister(105, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
202
    ev.AddRegister(106, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
203
    ev.AddRegister(107, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
204
    ev.AddRegister(108, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
205
    ev.AddRegister(109, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
206
    ev.AddRegister(110, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
207
    ev.AddRegister(111, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
208
    ev.AddRegister(112, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
209

210
    EXPECT_NO_THROW(ev.SendRequests());
2✔
211

212
    EXPECT_EQ(port.Requests.size(), 1);
2✔
213

214
    auto request = port.Requests.front();
4✔
215

216
    EXPECT_EQ(request.size(), 26);
2✔
217
    EXPECT_EQ(request[0], 0x0A); // slave id
2✔
218
    EXPECT_EQ(request[1], 0x46); // command
2✔
219
    EXPECT_EQ(request[2], 0x18); // subcommand
2✔
220
    EXPECT_EQ(request[3], 0x14); // settings size
2✔
221

222
    EXPECT_EQ(request[4], 0x01); // event type
2✔
223
    EXPECT_EQ(request[5], 0x00); // address MSB
2✔
224
    EXPECT_EQ(request[6], 0x65); // address LSB
2✔
225
    EXPECT_EQ(request[7], 0x02); // count
2✔
226
    EXPECT_EQ(request[8], 0x02); // priority 101
2✔
227
    EXPECT_EQ(request[9], 0x02); // priority 102
2✔
228

229
    EXPECT_EQ(request[10], 0x04); // event type
2✔
230
    EXPECT_EQ(request[11], 0x00); // address MSB
2✔
231
    EXPECT_EQ(request[12], 0x67); // address LSB
2✔
232
    EXPECT_EQ(request[13], 0x0A); // count
2✔
233
    EXPECT_EQ(request[14], 0x01); // priority 103
2✔
234
    EXPECT_EQ(request[15], 0x01); // priority 104
2✔
235
    EXPECT_EQ(request[16], 0x01); // priority 105
2✔
236
    EXPECT_EQ(request[17], 0x01); // priority 106
2✔
237
    EXPECT_EQ(request[18], 0x01); // priority 107
2✔
238
    EXPECT_EQ(request[19], 0x01); // priority 108
2✔
239
    EXPECT_EQ(request[20], 0x01); // priority 109
2✔
240
    EXPECT_EQ(request[21], 0x02); // priority 110
2✔
241
    EXPECT_EQ(request[22], 0x02); // priority 111
2✔
242
    EXPECT_EQ(request[23], 0x02); // priority 112
2✔
243

244
    EXPECT_EQ(request[24], 0x25); // CRC16 LSB
2✔
245
    EXPECT_EQ(request[25], 0xF0); // CRC16 MSB
2✔
246

247
    EXPECT_EQ(response.size(), 12);
2✔
248
    EXPECT_TRUE(response[101]);
2✔
249
    EXPECT_TRUE(response[102]);
2✔
250
    EXPECT_TRUE(response[103]);
2✔
251
    EXPECT_TRUE(response[104]);
2✔
252
    EXPECT_FALSE(response[105]);
2✔
253
    EXPECT_TRUE(response[106]);
2✔
254
    EXPECT_TRUE(response[107]);
2✔
255
    EXPECT_TRUE(response[108]);
2✔
256
    EXPECT_TRUE(response[109]);
2✔
257
    EXPECT_TRUE(response[110]);
2✔
258
    EXPECT_FALSE(response[111]);
2✔
259
    EXPECT_TRUE(response[112]);
2✔
260
}
2✔
261

262
TEST(TModbusExtTest, EventsEnablerRangesWithHoles)
8✔
263
{
264
    TPortMock port;
4✔
265
    // 0x03 = 0b00000011
266
    // 0xF3 = 0b11110011
267
    // 0x02 = 0b00000010
268
    // 0x03 = 0b00000011
269
    port.AddResponse({0x0A, 0x46, 0x18, 0x04, 0x03, 0xF3, 0x02, 0x03, 0xA4, 0xBE});
2✔
270

271
    std::map<uint16_t, bool> response;
4✔
272
    Modbus::TModbusRTUTraits traits;
4✔
273
    ModbusExt::TEventsEnabler ev(
274
        10,
275
        port,
276
        traits,
277
        [&response](uint8_t type, uint16_t reg, bool enabled) { response[reg] = enabled; },
14✔
278
        ModbusExt::TEventsEnabler::DISABLE_EVENTS_IN_HOLES);
4✔
279

280
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
281
    ev.AddRegister(102, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
282
    ev.AddRegister(103, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
283
    ev.AddRegister(104, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
284
    ev.AddRegister(107, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
285
    ev.AddRegister(108, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
286
    ev.AddRegister(109, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
287
    ev.AddRegister(110, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
288
    ev.AddRegister(111, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
289
    ev.AddRegister(112, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
290
    ev.AddRegister(118, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
291
    ev.AddRegister(119, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
292

293
    EXPECT_NO_THROW(ev.SendRequests());
2✔
294

295
    EXPECT_EQ(port.Requests.size(), 1);
2✔
296

297
    auto request = port.Requests.front();
4✔
298

299
    EXPECT_EQ(request.size(), 32);
2✔
300
    EXPECT_EQ(request[0], 0x0A); // slave id
2✔
301
    EXPECT_EQ(request[1], 0x46); // command
2✔
302
    EXPECT_EQ(request[2], 0x18); // subcommand
2✔
303
    EXPECT_EQ(request[3], 0x1A); // settings size
2✔
304

305
    EXPECT_EQ(request[4], 0x01); // event type
2✔
306
    EXPECT_EQ(request[5], 0x00); // address MSB
2✔
307
    EXPECT_EQ(request[6], 0x65); // address LSB
2✔
308
    EXPECT_EQ(request[7], 0x02); // count
2✔
309
    EXPECT_EQ(request[8], 0x02); // priority 101
2✔
310
    EXPECT_EQ(request[9], 0x02); // priority 102
2✔
311

312
    EXPECT_EQ(request[10], 0x04); // event type
2✔
313
    EXPECT_EQ(request[11], 0x00); // address MSB
2✔
314
    EXPECT_EQ(request[12], 0x67); // address LSB
2✔
315
    EXPECT_EQ(request[13], 0x0A); // count
2✔
316
    EXPECT_EQ(request[14], 0x01); // priority 103
2✔
317
    EXPECT_EQ(request[15], 0x01); // priority 104
2✔
318
    EXPECT_EQ(request[16], 0x00); // priority 105
2✔
319
    EXPECT_EQ(request[17], 0x00); // priority 106
2✔
320
    EXPECT_EQ(request[18], 0x01); // priority 107
2✔
321
    EXPECT_EQ(request[19], 0x01); // priority 108
2✔
322
    EXPECT_EQ(request[20], 0x01); // priority 109
2✔
323
    EXPECT_EQ(request[21], 0x02); // priority 110
2✔
324
    EXPECT_EQ(request[22], 0x02); // priority 111
2✔
325
    EXPECT_EQ(request[23], 0x02); // priority 112
2✔
326

327
    EXPECT_EQ(request[24], 0x04); // event type
2✔
328
    EXPECT_EQ(request[25], 0x00); // address MSB
2✔
329
    EXPECT_EQ(request[26], 0x76); // address LSB
2✔
330
    EXPECT_EQ(request[27], 0x02); // count
2✔
331
    EXPECT_EQ(request[28], 0x02); // priority 118
2✔
332
    EXPECT_EQ(request[29], 0x02); // priority 119
2✔
333

334
    EXPECT_EQ(request[30], 0x81); // CRC16 LSB
2✔
335
    EXPECT_EQ(request[31], 0x54); // CRC16 MSB
2✔
336

337
    EXPECT_EQ(response.size(), 14);
2✔
338
    EXPECT_TRUE(response[101]);
2✔
339
    EXPECT_TRUE(response[102]);
2✔
340
    EXPECT_TRUE(response[103]);
2✔
341
    EXPECT_TRUE(response[104]);
2✔
342
    EXPECT_FALSE(response[105]);
2✔
343
    EXPECT_FALSE(response[106]);
2✔
344
    EXPECT_TRUE(response[107]);
2✔
345
    EXPECT_TRUE(response[108]);
2✔
346
    EXPECT_TRUE(response[109]);
2✔
347
    EXPECT_TRUE(response[110]);
2✔
348
    EXPECT_FALSE(response[111]);
2✔
349
    EXPECT_TRUE(response[112]);
2✔
350
    EXPECT_EQ(response.count(113), 0);
2✔
351
    EXPECT_EQ(response.count(114), 0);
2✔
352
    EXPECT_EQ(response.count(115), 0);
2✔
353
    EXPECT_EQ(response.count(116), 0);
2✔
354
    EXPECT_EQ(response.count(117), 0);
2✔
355
    EXPECT_TRUE(response[118]);
2✔
356
    EXPECT_TRUE(response[119]);
2✔
357
}
2✔
358

359
TEST(TModbusExtTest, ReadEventsNoEventsNoConfirmation)
8✔
360
{
361
    TPortMock port;
4✔
362
    TTestEventsVisitor visitor;
4✔
363
    ModbusExt::TEventConfirmationState state;
2✔
364
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
365

366
    port.AddResponse({0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D}); // No events
2✔
367
    bool ret = true;
2✔
368
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, traits, std::chrono::milliseconds(100), 0, state, visitor));
2✔
369
    EXPECT_FALSE(ret);
2✔
370

371
    EXPECT_EQ(port.Requests.size(), 1);
2✔
372
    auto request = port.Requests.front();
4✔
373

374
    EXPECT_EQ(request.size(), 9);
2✔
375
    EXPECT_EQ(request[0], 0xFD); // broadcast
2✔
376
    EXPECT_EQ(request[1], 0x46); // command
2✔
377
    EXPECT_EQ(request[2], 0x10); // subcommand
2✔
378
    EXPECT_EQ(request[3], 0x00); // min slave id
2✔
379
    EXPECT_EQ(request[4], 0xF8); // max length
2✔
380
    EXPECT_EQ(request[5], 0x00); // slave id (confirmation)
2✔
381
    EXPECT_EQ(request[6], 0x00); // flag (confirmation)
2✔
382

383
    EXPECT_EQ(request[7], 0x79); // CRC16 LSB
2✔
384
    EXPECT_EQ(request[8], 0x5B); // CRC16 MSB
2✔
385

386
    EXPECT_EQ(visitor.Events.size(), 0);
2✔
387
}
2✔
388

389
TEST(TModbusExtTest, ReadEventsWithConfirmation)
8✔
390
{
391
    TPortMock port;
4✔
392
    TTestEventsVisitor visitor;
4✔
393
    ModbusExt::TEventConfirmationState state;
2✔
394
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
395

396
    port.AddResponse(
2✔
397
        {0xFF, 0xFF, 0xFF, 0x05, 0x46, 0x11, 0x01, 0x01, 0x06, 0x02, 0x04, 0x01, 0xD0, 0x04, 0x00, 0x2B, 0xAC});
4✔
398
    bool ret = false;
2✔
399
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, traits, std::chrono::milliseconds(100), 0, state, visitor));
2✔
400
    EXPECT_TRUE(ret);
2✔
401

402
    EXPECT_EQ(port.Requests.size(), 1);
2✔
403
    auto request = port.Requests.front();
4✔
404

405
    EXPECT_EQ(request.size(), 9);
2✔
406
    EXPECT_EQ(request[0], 0xFD); // slave id
2✔
407
    EXPECT_EQ(request[1], 0x46); // command
2✔
408
    EXPECT_EQ(request[2], 0x10); // subcommand
2✔
409
    EXPECT_EQ(request[3], 0x00); // min slave id
2✔
410
    EXPECT_EQ(request[4], 0xF8); // max length
2✔
411
    EXPECT_EQ(request[5], 0x00); // slave id (confirmation)
2✔
412
    EXPECT_EQ(request[6], 0x00); // flag (confirmation)
2✔
413

414
    EXPECT_EQ(request[7], 0x79); // CRC16 LSB
2✔
415
    EXPECT_EQ(request[8], 0x5B); // CRC16 MSB
2✔
416

417
    EXPECT_EQ(visitor.Events.size(), 1);
2✔
418
    EXPECT_EQ(visitor.Events[464], 4);
2✔
419

420
    port.AddResponse({0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D}); // No events
2✔
421
    visitor.Events.clear();
2✔
422
    port.Requests.clear();
2✔
423
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, traits, std::chrono::milliseconds(5), 5, state, visitor));
2✔
424
    EXPECT_FALSE(ret);
2✔
425

426
    EXPECT_EQ(port.Requests.size(), 1);
2✔
427
    request = port.Requests.front();
2✔
428

429
    EXPECT_EQ(request.size(), 9);
2✔
430
    EXPECT_EQ(request[0], 0xFD); // slave id
2✔
431
    EXPECT_EQ(request[1], 0x46); // command
2✔
432
    EXPECT_EQ(request[2], 0x10); // subcommand
2✔
433
    EXPECT_EQ(request[3], 0x05); // min slave id
2✔
434
    EXPECT_EQ(request[4], 0x2C); // max length
2✔
435
    EXPECT_EQ(request[5], 0x05); // slave id (confirmation)
2✔
436
    EXPECT_EQ(request[6], 0x01); // flag (confirmation)
2✔
437

438
    EXPECT_EQ(request[7], 0xFB); // CRC16 LSB
2✔
439
    EXPECT_EQ(request[8], 0x3F); // CRC16 MSB
2✔
440

441
    EXPECT_EQ(visitor.Events.size(), 0);
2✔
442
}
2✔
443

444
TEST(TModbusExtTest, ReadEventsReboot)
8✔
445
{
446
    TPortMock port;
4✔
447
    TTestEventsVisitor visitor;
4✔
448
    ModbusExt::TEventConfirmationState state;
2✔
449
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
450

451
    port.AddResponse(
2✔
452
        {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x5E}); // Reboot event
4✔
453
    bool ret = false;
2✔
454
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, traits, std::chrono::milliseconds(100), 0, state, visitor));
2✔
455
    EXPECT_TRUE(ret);
2✔
456

457
    EXPECT_TRUE(visitor.Reboot);
2✔
458
}
2✔
459

460
TEST(TModbusExtTest, GetRTUPacketStart)
8✔
461
{
462
    uint8_t goodPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x5E};
2✔
463
    uint8_t goodPacketWithGlitch[] = {0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D};
2✔
464
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(goodPacket, sizeof(goodPacket)), goodPacket + 3);
2✔
465
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(goodPacketWithGlitch, sizeof(goodPacketWithGlitch)),
2✔
466
              goodPacketWithGlitch + 5);
467

468
    uint8_t notEnoughDataPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00};
2✔
469
    uint8_t badCrcPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x11, 0x5E};
2✔
470
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(notEnoughDataPacket, sizeof(notEnoughDataPacket)), nullptr);
2✔
471
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(badCrcPacket, sizeof(badCrcPacket)), nullptr);
2✔
472
}
2✔
473

474
class TModbusExtTraitsTest: public testing::Test
475
{
476
public:
477
    void TestEqual(const std::vector<uint8_t>& b1, const std::vector<uint8_t>& b2) const
1✔
478
    {
479
        ASSERT_EQ(b1.size(), b2.size());
1✔
480

481
        for (size_t i = 0; i < b1.size(); ++i) {
6✔
482
            ASSERT_EQ(b1[i], b2[i]) << i;
5✔
483
        }
484
    }
485
};
486

487
TEST_F(TModbusExtTraitsTest, ReadFrameGood)
8✔
488
{
489
    TPortMock port;
2✔
490
    port.AddResponse({0xfd, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x1c, 0xef});
2✔
491
    ModbusExt::TModbusTraits traits(std::make_unique<Modbus::TModbusRTUTraits>());
4✔
492
    traits.SetSn(0xfecae7e5);
2✔
493
    std::chrono::milliseconds t(10);
2✔
494

495
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
496

497
    auto resp = traits.Transaction(port, 0xfd, req, req.size(), t, t);
2✔
498
    ASSERT_EQ(resp.Pdu.size(), req.size());
2✔
499

500
    TestEqual(resp.Pdu, req);
2✔
501
}
502

503
TEST_F(TModbusExtTraitsTest, ReadFrameTooSmallError)
8✔
504
{
505
    TPortMock port;
2✔
506
    port.AddResponse({0xfd, 0x46, 0x09, 0xfe, 0xca});
2✔
507
    ModbusExt::TModbusTraits traits(std::make_unique<Modbus::TModbusRTUTraits>());
4✔
508
    traits.SetSn(0xfecae7e5);
2✔
509
    std::chrono::milliseconds t(10);
2✔
510

511
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
512

513
    ASSERT_THROW(traits.Transaction(port, 0xfd, req, req.size(), t, t), Modbus::TMalformedResponseError);
2✔
514
}
515

516
TEST_F(TModbusExtTraitsTest, ReadFrameInvalidCrc)
8✔
517
{
518
    TPortMock port;
2✔
519
    port.AddResponse({0xfd, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x10, 0xef});
2✔
520
    ModbusExt::TModbusTraits traits(std::make_unique<Modbus::TModbusRTUTraits>());
4✔
521
    traits.SetSn(0xfecae7e5);
2✔
522
    std::chrono::milliseconds t(10);
2✔
523

524
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
525

526
    ASSERT_THROW(traits.Transaction(port, 0, req, req.size(), t, t), Modbus::TMalformedResponseError);
2✔
527
}
528

529
TEST_F(TModbusExtTraitsTest, ReadFrameInvalidHeader)
8✔
530
{
531
    TPortMock port;
2✔
532
    std::vector<uint8_t> badHeader =
533
        {0xfe, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x00, 0x00};
2✔
534
    port.AddResponse(badHeader);
2✔
535

536
    ModbusExt::TModbusTraits traits(std::make_unique<Modbus::TModbusRTUTraits>());
4✔
537
    traits.SetSn(0xfecae7e5);
2✔
538
    std::chrono::milliseconds t(10);
2✔
539

540
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
541

542
    ASSERT_THROW(
4✔
543
        {
544
            try {
545
                traits.Transaction(port, 0, req, req.size(), t, t);
546
            } catch (const Modbus::TUnexpectedResponseError& e) {
547
                EXPECT_STREQ("request and response slave id mismatch", e.what());
548
                throw;
549
            }
550
        },
551
        Modbus::TUnexpectedResponseError);
552

553
    badHeader[0] = 0xfd;
2✔
554
    badHeader[1] = 0x45;
2✔
555
    port.AddResponse(badHeader);
2✔
556
    ASSERT_THROW(
4✔
557
        {
558
            try {
559
                traits.Transaction(port, 0xfd, req, req.size(), t, t);
560
            } catch (const Modbus::TUnexpectedResponseError& e) {
561
                EXPECT_STREQ("invalid response command", e.what());
562
                throw;
563
            }
564
        },
565
        Modbus::TUnexpectedResponseError);
566

567
    badHeader[1] = 0x46;
2✔
568
    badHeader[2] = 0x10;
2✔
569
    port.AddResponse(badHeader);
2✔
570
    ASSERT_THROW(
4✔
571
        {
572
            try {
573
                traits.Transaction(port, 0xfd, req, req.size(), t, t);
574
            } catch (const Modbus::TUnexpectedResponseError& e) {
575
                EXPECT_STREQ("invalid response subcommand: 16", e.what());
576
                throw;
577
            }
578
        },
579
        Modbus::TUnexpectedResponseError);
580

581
    badHeader[2] = 0x09;
2✔
582
    badHeader[3] = 0x01;
2✔
583
    port.AddResponse(badHeader);
2✔
584
    ASSERT_THROW(
4✔
585
        {
586
            try {
587
                traits.Transaction(port, 0xfd, req, req.size(), t, t);
588
            } catch (const Modbus::TUnexpectedResponseError& e) {
589
                EXPECT_STREQ("SN mismatch: got 30074853, wait 4274710501", e.what());
590
                throw;
591
            }
592
        },
593
        Modbus::TUnexpectedResponseError);
594
}
595

596
TEST(TModbusExtTest, ScanStartDeviceFound)
8✔
597
{
598
    TPortMock port;
2✔
599
    // DEVICE_FOUND_RESPONSE_SCAN_COMMAND (0x03)
600
    port.AddResponse({0xFD, 0x46, 0x03, 0x12, 0x34, 0x56, 0x78, 0x05, 0x00, 0x00});
2✔
601
    // NO_MORE_DEVICES_RESPONSE_SCAN_COMMAND
602
    port.AddResponse({0xFD, 0x46, 0x04, 0x00, 0x00});
2✔
603

604
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
2✔
605
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
2✔
606
    EXPECT_NO_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices));
2✔
607
    ASSERT_EQ(scannedDevices.size(), 1);
2✔
608
    EXPECT_EQ(scannedDevices[0].SlaveId, 5);
2✔
609
    EXPECT_EQ(scannedDevices[0].Sn, 0x12345678);
2✔
610
}
611

612
TEST(TModbusExtTest, ScanStartNoMoreDevices)
8✔
613
{
614
    TPortMock port;
4✔
615
    // NO_MORE_DEVICES_RESPONSE_SCAN_COMMAND (0x04)
616
    port.AddResponse({0xFD, 0x46, 0x04, 0x00, 0x00});
2✔
617

618
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
619
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
4✔
620
    EXPECT_NO_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices));
2✔
621
    EXPECT_TRUE(scannedDevices.empty());
2✔
622
}
2✔
623

624
TEST(TModbusExtTest, ScanMultipleDevices)
8✔
625
{
626
    TPortMock port;
2✔
627
    port.AddResponse(
2✔
628
        {0xFD, 0x46, 0x03, 0x01, 0x02, 0x03, 0x04, 0x10, 0x00, 0x00}); // DEVICE_FOUND_RESPONSE_SCAN_COMMAND
4✔
629
    port.AddResponse(
2✔
630
        {0xFD, 0x46, 0x03, 0x05, 0x06, 0x07, 0x08, 0x20, 0x00, 0x00}); // DEVICE_FOUND_RESPONSE_SCAN_COMMAND
4✔
631
    port.AddResponse({0xFD, 0x46, 0x04, 0x00, 0x00});                  // NO_MORE_DEVICES_RESPONSE_SCAN_COMMAND
2✔
632

633
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
2✔
634
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
2✔
635
    EXPECT_NO_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices));
2✔
636
    ASSERT_EQ(scannedDevices.size(), 2);
2✔
637
    EXPECT_EQ(scannedDevices[0].SlaveId, 0x10);
2✔
638
    EXPECT_EQ(scannedDevices[0].Sn, 0x01020304);
2✔
639
    EXPECT_EQ(scannedDevices[1].SlaveId, 0x20);
2✔
640
    EXPECT_EQ(scannedDevices[1].Sn, 0x05060708);
2✔
641
}
642

643
TEST(TModbusExtTest, ScanStartTimeout)
8✔
644
{
645
    TPortMock port;
4✔
646
    port.ThrowTimeout = true;
2✔
647

648
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
649
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
4✔
650
    EXPECT_NO_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices));
2✔
651
    EXPECT_TRUE(scannedDevices.empty());
2✔
652
}
2✔
653

654
TEST(TModbusExtTest, ScanInvalidSubCommand)
8✔
655
{
656
    TPortMock port;
4✔
657
    port.AddResponse({0xFD, 0x46, 0x12, 0x00, 0x00});
2✔
658

659
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
4✔
660
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
4✔
661
    EXPECT_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices),
2✔
662
                 Modbus::TMalformedResponseError);
663
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc