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

wirenboard / wb-mqtt-serial / 2

29 Dec 2025 12:28PM UTC coverage: 76.817% (+4.0%) from 72.836%
2

Pull #1045

github

54aa0c
pgasheev
up changelog
Pull Request #1045: Fix firmware version in WB-M1W2 template

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

1651.61 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)
22✔
12
    {
13
        if (data.size() < 2) {
22✔
14
            return;
×
15
        }
16
        auto crc = CRC16::CalculateCRC16(data.data(), data.size() - 2);
22✔
17
        data[data.size() - 1] = crc & 0xFF;
22✔
18
        data[data.size() - 2] = (crc >> 8) & 0xFF;
22✔
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()
32✔
29
        {}
32✔
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
46✔
43
        {
44
            std::vector<uint8_t> data(buf, buf + count);
92✔
45
            Requests.emplace_back(std::move(data));
46✔
46
        }
46✔
47

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

53
        TReadFrameResult ReadFrame(uint8_t* buf,
46✔
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) {
46✔
60
                throw TResponseTimeoutException();
2✔
61
            }
62
            if (Responses.empty()) {
44✔
63
                throw TResponseTimeoutException();
×
64
            }
65
            if (Responses.front().size() > count) {
44✔
66
                throw std::runtime_error("Buffer is too small");
×
67
            }
68
            TReadFrameResult res;
44✔
69
            res.Count = Responses.front().size();
44✔
70
            memcpy(buf, Responses.front().data(), Responses.front().size());
44✔
71
            Responses.pop_front();
44✔
72
            return res;
44✔
73
        }
74

75
        void SkipNoise() override
×
76
        {}
77

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

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

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

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

100
        void AddResponse(const std::vector<uint8_t>& data)
44✔
101
        {
102
            Responses.push_back(data);
44✔
103
            if (Responses.back().size() >= 2 && Responses.back()[Responses.back().size() - 2] == 0 &&
66✔
104
                Responses.back()[Responses.back().size() - 1] == 0)
22✔
105
            {
106
                SetCrc(Responses.back());
22✔
107
            }
108
        }
44✔
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,
4✔
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) {
4✔
124
                Reboot = true;
2✔
125
                return;
2✔
126
            }
127
            Events[eventId] = data[0] | (data[1] << 8);
2✔
128
        }
129
    };
130

131
} // namespace
132

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

262
TEST(TModbusExtTest, EventsEnablerRangesWithHoles)
16✔
263
{
264
    TPortMock port;
8✔
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});
4✔
270

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

468
    uint8_t notEnoughDataPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00};
4✔
469
    uint8_t badCrcPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x11, 0x5E};
4✔
470
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(notEnoughDataPacket, sizeof(notEnoughDataPacket)), nullptr);
4✔
471
    EXPECT_EQ(ModbusExt::GetRTUPacketStart(badCrcPacket, sizeof(badCrcPacket)), nullptr);
4✔
472
}
4✔
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
2✔
478
    {
479
        ASSERT_EQ(b1.size(), b2.size());
2✔
480

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

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

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

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

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

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

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

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

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

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

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

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

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

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

542
    ASSERT_THROW(
8✔
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;
4✔
554
    badHeader[1] = 0x45;
4✔
555
    port.AddResponse(badHeader);
4✔
556
    ASSERT_THROW(
8✔
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;
4✔
568
    badHeader[2] = 0x10;
4✔
569
    port.AddResponse(badHeader);
4✔
570
    ASSERT_THROW(
8✔
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;
4✔
582
    badHeader[3] = 0x01;
4✔
583
    port.AddResponse(badHeader);
4✔
584
    ASSERT_THROW(
8✔
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)
16✔
597
{
598
    TPortMock port;
4✔
599
    // DEVICE_FOUND_RESPONSE_SCAN_COMMAND (0x03)
600
    port.AddResponse({0xFD, 0x46, 0x03, 0x12, 0x34, 0x56, 0x78, 0x05, 0x00, 0x00});
4✔
601
    // NO_MORE_DEVICES_RESPONSE_SCAN_COMMAND
602
    port.AddResponse({0xFD, 0x46, 0x04, 0x00, 0x00});
4✔
603

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

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

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

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

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

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

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

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

659
    ModbusExt::TModbusRTUWithArbitrationTraits traits;
8✔
660
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
8✔
661
    EXPECT_THROW(ModbusExt::Scan(port, traits, ModbusExt::TModbusExtCommand::ACTUAL, scannedDevices),
4✔
662
                 Modbus::TMalformedResponseError);
663
}
4✔
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