• 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

96.03
/test/serial_client_test.cpp
1
#include "fake_serial_device.h"
2
#include "fake_serial_port.h"
3
#include "log.h"
4
#include "rpc/rpc_port_handler.h"
5
#include "rpc/rpc_port_load_raw_serial_client_task.h"
6
#include "serial_driver.h"
7

8
#include <wblib/driver_args.h>
9
#include <wblib/testing/fake_driver.h>
10
#include <wblib/testing/fake_mqtt.h>
11

12
#include <algorithm>
13
#include <cassert>
14
#include <filesystem>
15
#include <gtest/gtest.h>
16
#include <map>
17
#include <memory>
18
#include <string>
19

20
#include "port/tcp_port_settings.h"
21

22
using namespace std;
23
using namespace WBMQTT;
24
using namespace WBMQTT::Testing;
25

26
#define LOG(logger) ::logger.Log() << "[serial client test] "
27

28
namespace
29
{
30
    const auto DB_PATH = "/tmp/wb-mqtt-serial-test.db";
31

32
    std::string GetTextValue(PRegister reg)
532✔
33
    {
34
        return ConvertFromRawValue(*reg->GetConfig(), reg->GetValue());
1,064✔
35
    }
36
}
37

38
class TSerialClientTest: public TLoggedFixture
39
{
40
    std::unordered_map<PRegister, std::string> LastRegValues;
41
    std::unordered_map<PRegister, TRegister::TErrorState> LastRegErrors;
42

43
    void EmitErrorMsg(PRegister reg)
412✔
44
    {
45
        if (LastRegErrors.count(reg) && LastRegErrors[reg] == reg->GetErrorState()) {
412✔
46
            return;
288✔
47
        }
48
        std::string what;
248✔
49
        const std::vector<std::string> errorNames = {"read", "write", "poll interval miss"};
744✔
50
        for (size_t i = 0; i < TRegister::TError::MAX_ERRORS; ++i) {
496✔
51
            if (reg->GetErrorState().test(i)) {
372✔
52
                if (!what.empty()) {
26✔
53
                    what += "+";
4✔
54
                }
55
                if (i < errorNames.size()) {
26✔
56
                    what += errorNames[i];
26✔
57
                } else {
58
                    what += "unknown";
×
59
                }
60
            }
61
        }
62
        if (what.empty()) {
124✔
63
            what = "no";
102✔
64
        }
65
        Emit() << "Error Callback: <" << reg->Device()->ToString() << ":" << reg->GetConfig()->TypeName << ": "
248✔
66
               << reg->GetConfig()->GetAddress() << ">: " << what << " error";
124✔
67
        LastRegErrors[reg] = reg->GetErrorState();
124✔
68
    }
69

70
protected:
71
    TSerialClientTest()
86✔
72
    {
86✔
73
        PortOpenCloseSettings.ReopenTimeout = std::chrono::milliseconds(0);
86✔
74
    }
86✔
75

76
    void SetUp();
77
    void TearDown();
78
    PRegister Reg(int addr,
100✔
79
                  RegisterFormat fmt = U16,
80
                  double scale = 1,
81
                  double offset = 0,
82
                  double round_to = 0,
83
                  EWordOrder word_order = EWordOrder::BigEndian,
84
                  EByteOrder byte_order = EByteOrder::BigEndian,
85
                  uint32_t dataOffset = 0,
86
                  uint32_t dataBitWidth = 0)
87
    {
88
        return Device->AddRegister(TRegisterConfig::Create(TFakeSerialDevice::REG_FAKE,
200✔
89
                                                           addr,
90
                                                           fmt,
91
                                                           scale,
92
                                                           offset,
93
                                                           round_to,
94
                                                           TRegisterConfig::TSporadicMode::DISABLED,
95
                                                           false,
96
                                                           "fake",
97
                                                           word_order,
98
                                                           byte_order,
99
                                                           dataOffset,
100
                                                           dataBitWidth));
300✔
101
    }
102
    PFakeSerialPort Port;
103
    PSerialClient SerialClient;
104
    PFakeSerialDevice Device;
105
    TSerialDeviceFactory DeviceFactory;
106
    PRPCConfig rpcConfig;
107

108
    bool HasSetupRegisters = false;
109
    TPortOpenCloseLogic::TSettings PortOpenCloseSettings;
110
};
111

112
class TSerialClientReopenTest: public TSerialClientTest
113
{
114
public:
115
    TSerialClientReopenTest()
2✔
116
    {
2✔
117
        PortOpenCloseSettings.ReopenTimeout = std::chrono::milliseconds(700);
2✔
118
    }
2✔
119
};
120

121
class TSerialClientTestWithSetupRegisters: public TSerialClientTest
122
{
123
public:
124
    TSerialClientTestWithSetupRegisters()
4✔
125
    {
4✔
126
        HasSetupRegisters = true;
4✔
127
    }
4✔
128
};
129

130
void TSerialClientTest::SetUp()
86✔
131
{
132
    RegisterProtocols(DeviceFactory);
86✔
133
    TFakeSerialDevice::Register(DeviceFactory);
86✔
134

135
    TLoggedFixture::SetUp();
86✔
136
    Port = std::make_shared<TFakeSerialPort>(*this, "<TSerialClientTest>");
86✔
137
    auto config = std::make_shared<TDeviceConfig>("fake_sample", "1", "fake");
86✔
138
    config->MaxReadRegisters = 0;
86✔
139

140
    config->FrameTimeout = std::chrono::milliseconds(100);
86✔
141
    Device = std::make_shared<TFakeSerialDevice>(config, DeviceFactory.GetProtocol("fake"));
86✔
142
    if (HasSetupRegisters) {
86✔
143
        PRegisterConfig reg1 = TRegisterConfig::Create(TFakeSerialDevice::REG_FAKE, 100);
12✔
144
        Device->AddSetupItem(PDeviceSetupItemConfig(new TDeviceSetupItemConfig("setup1", reg1, "10")));
4✔
145

146
        PRegisterConfig reg2 = TRegisterConfig::Create(TFakeSerialDevice::REG_FAKE, 101);
12✔
147
        Device->AddSetupItem(PDeviceSetupItemConfig(new TDeviceSetupItemConfig("setup2", reg2, "11")));
4✔
148

149
        PRegisterConfig reg3 = TRegisterConfig::Create(TFakeSerialDevice::REG_FAKE, 102);
12✔
150
        Device->AddSetupItem(PDeviceSetupItemConfig(new TDeviceSetupItemConfig("setup3", reg3, "12")));
4✔
151
    }
152
    Device->SetFakePort(Port);
86✔
153
    SerialClient = std::make_shared<TSerialClient>(std::make_shared<TFeaturePort>(Port, false),
172✔
154
                                                   PortOpenCloseSettings,
86✔
155
                                                   std::chrono::steady_clock::now);
86✔
156
    SerialClient->SetReadCallback([this](PRegister reg) {
172✔
157
        if (reg->GetErrorState().count()) {
370✔
158
            EmitErrorMsg(reg);
2✔
159
        }
160
        std::string value = GetTextValue(reg);
740✔
161
        bool unchanged = (LastRegValues.count(reg) && LastRegValues[reg] == value);
370✔
162
        Emit() << "Read Callback: <" << reg->Device()->ToString() << ":" << reg->GetConfig()->TypeName << ": "
740✔
163
               << reg->GetConfig()->GetAddress() << "> becomes " << value << (unchanged ? " [unchanged]" : "");
370✔
164
        LastRegValues[reg] = value;
370✔
165
        if (!reg->GetErrorState().count()) {
370✔
166
            EmitErrorMsg(reg);
368✔
167
        }
168
    });
542✔
169
    SerialClient->SetErrorCallback([this](PRegister reg) { EmitErrorMsg(reg); });
128✔
170
}
86✔
171

172
void TSerialClientTest::TearDown()
86✔
173
{
174
    TLoggedFixture::TearDown();
86✔
175
    TFakeSerialDevice::ClearDevices();
86✔
176
}
86✔
177

178
TEST_F(TSerialClientTest, PortOpenError)
16✔
179
{
180
    // The test checks recovery logic after port opening failure
181
    // TSerialClient must try to open port during every cycle and set /meta/error for controls
182

183
    PRegister reg0 = Reg(0, U8);
8✔
184
    PRegister reg1 = Reg(1, U8);
8✔
185

186
    SerialClient->AddDevice(Device);
4✔
187

188
    Port->SetAllowOpen(false);
4✔
189

190
    Note() << "Cycle() [port open error]";
4✔
191
    SerialClient->Cycle();
4✔
192

193
    Note() << "Cycle() [port open error2]";
4✔
194
    SerialClient->Cycle();
4✔
195

196
    Port->SetAllowOpen(true);
4✔
197

198
    // Disconnected device poll delayed for 500ms on every poll retry
199
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
4✔
200

201
    Note() << "Cycle() [successful port open]";
4✔
202
    SerialClient->Cycle();
4✔
203
}
4✔
204

205
TEST_F(TSerialClientReopenTest, ReopenTimeout)
16✔
206
{
207
    // The test checks recovery logic after port opening failure
208
    // TSerialClient must try to open port during every cycle and set /meta/error for controls
209

210
    PRegister reg0 = Reg(0, U8);
8✔
211
    PRegister reg1 = Reg(1, U8);
8✔
212

213
    SerialClient->AddDevice(Device);
4✔
214

215
    Port->SetAllowOpen(false);
4✔
216

217
    Note() << "Cycle() [port open error]";
4✔
218
    SerialClient->Cycle();
4✔
219

220
    Port->SetAllowOpen(true);
4✔
221

222
    // The opening will be unsuccessful because of 700ms timeout since last failed open
223
    Note() << "Cycle() [port open error2]";
4✔
224
    SerialClient->Cycle();
4✔
225

226
    std::this_thread::sleep_for(PortOpenCloseSettings.ReopenTimeout);
4✔
227

228
    Note() << "Cycle() [successful port open]";
4✔
229
    SerialClient->Cycle();
4✔
230
}
4✔
231

232
TEST_F(TSerialClientTest, Poll)
16✔
233
{
234

235
    PRegister reg0 = Reg(0, U8);
8✔
236
    PRegister reg1 = Reg(1, U8);
8✔
237
    PRegister discrete10 = Reg(10);
8✔
238
    PRegister reg22 = Reg(22);
8✔
239
    PRegister reg33 = Reg(33);
8✔
240

241
    SerialClient->AddDevice(Device);
4✔
242

243
    Note() << "Cycle()";
4✔
244
    SerialClient->Cycle();
4✔
245

246
    Device->Registers[1] = 1;
4✔
247
    Device->Registers[10] = 1;
4✔
248
    Device->Registers[22] = 4242;
4✔
249
    Device->Registers[33] = 42000;
4✔
250

251
    Note() << "Cycle()";
4✔
252
    SerialClient->Cycle();
4✔
253

254
    EXPECT_EQ(to_string(0), GetTextValue(reg0));
8✔
255
    EXPECT_EQ(to_string(1), GetTextValue(reg1));
8✔
256
    EXPECT_EQ(to_string(1), GetTextValue(discrete10));
8✔
257
    EXPECT_EQ(to_string(4242), GetTextValue(reg22));
8✔
258
    EXPECT_EQ(to_string(42000), GetTextValue(reg33));
8✔
259
}
4✔
260

261
TEST_F(TSerialClientTest, Write)
16✔
262
{
263
    PRegister reg1 = Reg(1);
8✔
264
    PRegister reg20 = Reg(20);
8✔
265
    SerialClient->AddDevice(Device);
4✔
266

267
    Note() << "Cycle()";
4✔
268
    SerialClient->Cycle();
4✔
269

270
    SerialClient->SetTextValue(reg1, "1");
4✔
271
    SerialClient->SetTextValue(reg20, "4242");
4✔
272

273
    for (int i = 0; i < 3; ++i) {
16✔
274
        Note() << "Cycle()";
12✔
275
        SerialClient->Cycle();
12✔
276

277
        EXPECT_EQ(to_string(1), GetTextValue(reg1));
24✔
278
        EXPECT_EQ(1, Device->Registers[1]);
12✔
279
        EXPECT_EQ(to_string(4242), GetTextValue(reg20));
24✔
280
        EXPECT_EQ(4242, Device->Registers[20]);
12✔
281
    }
282
}
4✔
283

284
TEST_F(TSerialClientTest, U8)
16✔
285
{
286
    PRegister reg20 = Reg(20, U8);
8✔
287
    PRegister reg30 = Reg(30, U8);
8✔
288
    SerialClient->AddDevice(Device);
4✔
289

290
    Note() << "server -> client: 10, 20";
4✔
291
    Device->Registers[20] = 10;
4✔
292
    Device->Registers[30] = 20;
4✔
293
    Note() << "Cycle()";
4✔
294
    SerialClient->Cycle();
4✔
295
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
296
    EXPECT_EQ(to_string(20), GetTextValue(reg30));
8✔
297

298
    Note() << "server -> client: 0x2010, 0x2011";
4✔
299
    Device->Registers[20] = 0x2010;
4✔
300
    Device->Registers[30] = 0x2011;
4✔
301
    Note() << "Cycle()";
4✔
302
    SerialClient->Cycle();
4✔
303
    EXPECT_EQ(to_string(0x10), GetTextValue(reg20));
8✔
304
    EXPECT_EQ(to_string(0x11), GetTextValue(reg30));
8✔
305

306
    Note() << "client -> server: 10";
4✔
307
    SerialClient->SetTextValue(reg20, "10");
4✔
308
    Note() << "Cycle()";
4✔
309
    SerialClient->Cycle();
4✔
310
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
311
    EXPECT_EQ(10, Device->Registers[20]);
4✔
312

313
    Note() << "client -> server: 257";
4✔
314
    SerialClient->SetTextValue(reg20, "257");
4✔
315
    Note() << "Cycle()";
4✔
316
    SerialClient->Cycle();
4✔
317
    EXPECT_EQ(to_string(1), GetTextValue(reg20));
8✔
318
    EXPECT_EQ(1, Device->Registers[20]);
4✔
319
}
4✔
320

321
TEST_F(TSerialClientTest, S8)
16✔
322
{
323
    PRegister reg20 = Reg(20, S8);
8✔
324
    PRegister reg30 = Reg(30, S8);
8✔
325
    SerialClient->AddDevice(Device);
4✔
326

327
    Note() << "server -> client: 10, 20";
4✔
328
    Device->Registers[20] = 10;
4✔
329
    Device->Registers[30] = 20;
4✔
330
    Note() << "Cycle()";
4✔
331
    SerialClient->Cycle();
4✔
332
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
333
    EXPECT_EQ(to_string(20), GetTextValue(reg30));
8✔
334

335
    Note() << "server -> client: -2, -3";
4✔
336
    Device->Registers[20] = 254;
4✔
337
    Device->Registers[30] = 253;
4✔
338
    Note() << "Cycle()";
4✔
339
    SerialClient->Cycle();
4✔
340
    EXPECT_EQ(to_string(-2), GetTextValue(reg20));
8✔
341
    EXPECT_EQ(to_string(-3), GetTextValue(reg30));
8✔
342

343
    Note() << "client -> server: 10";
4✔
344
    SerialClient->SetTextValue(reg20, "10");
4✔
345
    Note() << "Cycle()";
4✔
346
    SerialClient->Cycle();
4✔
347
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
348
    EXPECT_EQ(10, Device->Registers[20]);
4✔
349

350
    Note() << "client -> server: -2";
4✔
351
    SerialClient->SetTextValue(reg20, "-2");
4✔
352
    Note() << "Cycle()";
4✔
353
    SerialClient->Cycle();
4✔
354
    EXPECT_EQ(to_string(-2), GetTextValue(reg20));
8✔
355
    EXPECT_EQ(254, Device->Registers[20]);
4✔
356
}
4✔
357

358
TEST_F(TSerialClientTest, Char8)
16✔
359
{
360
    PRegister reg20 = Reg(20, Char8);
8✔
361
    PRegister reg30 = Reg(30, Char8);
8✔
362
    SerialClient->AddDevice(Device);
4✔
363

364
    Note() << "server -> client: 65, 66";
4✔
365
    Device->Registers[20] = 65;
4✔
366
    Device->Registers[30] = 66;
4✔
367
    Note() << "Cycle()";
4✔
368
    SerialClient->Cycle();
4✔
369
    EXPECT_EQ("A", GetTextValue(reg20));
8✔
370
    EXPECT_EQ("B", GetTextValue(reg30));
8✔
371

372
    Note() << "client -> server: '!'";
4✔
373
    SerialClient->SetTextValue(reg20, "!");
4✔
374
    Note() << "Cycle()";
4✔
375
    SerialClient->Cycle();
4✔
376
    EXPECT_EQ("!", GetTextValue(reg20));
8✔
377
    EXPECT_EQ(33, Device->Registers[20]);
4✔
378
}
4✔
379

380
TEST_F(TSerialClientTest, S64)
16✔
381
{
382
    PRegister reg20 = Reg(20, S64);
8✔
383
    PRegister reg30 = Reg(30, S64);
8✔
384
    SerialClient->AddDevice(Device);
4✔
385

386
    Note() << "server -> client: 10, 20";
4✔
387
    Device->Registers[20] = 0x00AA;
4✔
388
    Device->Registers[21] = 0x00BB;
4✔
389
    Device->Registers[22] = 0x00CC;
4✔
390
    Device->Registers[23] = 0x00DD;
4✔
391
    Device->Registers[30] = 0xFFFF;
4✔
392
    Device->Registers[31] = 0xFFFF;
4✔
393
    Device->Registers[32] = 0xFFFF;
4✔
394
    Device->Registers[33] = 0xFFFF;
4✔
395
    Note() << "Cycle()";
4✔
396
    SerialClient->Cycle();
4✔
397
    EXPECT_EQ(to_string(0x00AA00BB00CC00DD), GetTextValue(reg20));
8✔
398
    EXPECT_EQ(to_string(-1), GetTextValue(reg30));
8✔
399

400
    Note() << "client -> server: 10";
4✔
401
    SerialClient->SetTextValue(reg20, "10");
4✔
402
    Note() << "Cycle()";
4✔
403
    SerialClient->Cycle();
4✔
404
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
405
    EXPECT_EQ(0, Device->Registers[20]);
4✔
406
    EXPECT_EQ(0, Device->Registers[21]);
4✔
407
    EXPECT_EQ(0, Device->Registers[22]);
4✔
408
    EXPECT_EQ(10, Device->Registers[23]);
4✔
409

410
    Note() << "client -> server: -2";
4✔
411
    SerialClient->SetTextValue(reg20, "-2");
4✔
412
    Note() << "Cycle()";
4✔
413
    SerialClient->Cycle();
4✔
414
    EXPECT_EQ(to_string(-2), GetTextValue(reg20));
8✔
415
    EXPECT_EQ(0xFFFF, Device->Registers[20]);
4✔
416
    EXPECT_EQ(0xFFFF, Device->Registers[21]);
4✔
417
    EXPECT_EQ(0xFFFF, Device->Registers[22]);
4✔
418
    EXPECT_EQ(0xFFFE, Device->Registers[23]);
4✔
419
}
4✔
420

421
TEST_F(TSerialClientTest, U64)
16✔
422
{
423
    PRegister reg20 = Reg(20, U64);
8✔
424
    PRegister reg30 = Reg(30, U64);
8✔
425
    SerialClient->AddDevice(Device);
4✔
426

427
    Note() << "server -> client: 10, 20";
4✔
428
    Device->Registers[20] = 0x00AA;
4✔
429
    Device->Registers[21] = 0x00BB;
4✔
430
    Device->Registers[22] = 0x00CC;
4✔
431
    Device->Registers[23] = 0x00DD;
4✔
432
    Device->Registers[30] = 0xFFFF;
4✔
433
    Device->Registers[31] = 0xFFFF;
4✔
434
    Device->Registers[32] = 0xFFFF;
4✔
435
    Device->Registers[33] = 0xFFFF;
4✔
436
    Note() << "Cycle()";
4✔
437
    SerialClient->Cycle();
4✔
438
    EXPECT_EQ(to_string(0x00AA00BB00CC00DD), GetTextValue(reg20));
8✔
439
    EXPECT_EQ("18446744073709551615", GetTextValue(reg30));
8✔
440

441
    Note() << "client -> server: 10";
4✔
442
    SerialClient->SetTextValue(reg20, "10");
4✔
443
    Note() << "Cycle()";
4✔
444
    SerialClient->Cycle();
4✔
445
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
446
    EXPECT_EQ(0, Device->Registers[20]);
4✔
447
    EXPECT_EQ(0, Device->Registers[21]);
4✔
448
    EXPECT_EQ(0, Device->Registers[22]);
4✔
449
    EXPECT_EQ(10, Device->Registers[23]);
4✔
450

451
    Note() << "client -> server: -2";
4✔
452
    SerialClient->SetTextValue(reg20, "-2");
4✔
453
    Note() << "Cycle()";
4✔
454
    SerialClient->Cycle();
4✔
455
    EXPECT_EQ("18446744073709551614", GetTextValue(reg20));
8✔
456
    EXPECT_EQ(0xFFFF, Device->Registers[20]);
4✔
457
    EXPECT_EQ(0xFFFF, Device->Registers[21]);
4✔
458
    EXPECT_EQ(0xFFFF, Device->Registers[22]);
4✔
459
    EXPECT_EQ(0xFFFE, Device->Registers[23]);
4✔
460
}
4✔
461

462
TEST_F(TSerialClientTest, S32)
16✔
463
{
464
    PRegister reg20 = Reg(20, S32);
8✔
465
    PRegister reg30 = Reg(30, S32);
8✔
466
    // create scaled register
467
    PRegister reg24 = Reg(24, S32, 0.001);
8✔
468

469
    SerialClient->AddDevice(Device);
4✔
470

471
    Note() << "server -> client: 10, 20";
4✔
472
    Device->Registers[20] = 0x00AA;
4✔
473
    Device->Registers[21] = 0x00BB;
4✔
474
    Device->Registers[30] = 0xFFFF;
4✔
475
    Device->Registers[31] = 0xFFFF;
4✔
476
    Note() << "Cycle()";
4✔
477
    SerialClient->Cycle();
4✔
478
    EXPECT_EQ(to_string(0x00AA00BB), GetTextValue(reg20));
8✔
479
    EXPECT_EQ(to_string(-1), GetTextValue(reg30));
8✔
480

481
    Note() << "client -> server: 10";
4✔
482
    SerialClient->SetTextValue(reg20, "10");
4✔
483
    Note() << "Cycle()";
4✔
484
    SerialClient->Cycle();
4✔
485
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
486
    EXPECT_EQ(0, Device->Registers[20]);
4✔
487
    EXPECT_EQ(10, Device->Registers[21]);
4✔
488

489
    Note() << "client -> server: -2";
4✔
490
    SerialClient->SetTextValue(reg20, "-2");
4✔
491
    Note() << "Cycle()";
4✔
492
    SerialClient->Cycle();
4✔
493
    EXPECT_EQ(to_string(-2), GetTextValue(reg20));
8✔
494
    EXPECT_EQ(0xFFFF, Device->Registers[20]);
4✔
495
    EXPECT_EQ(0xFFFE, Device->Registers[21]);
4✔
496

497
    Note() << "client -> server: -0.123 (scaled)";
4✔
498
    SerialClient->SetTextValue(reg24, "-0.123");
4✔
499
    Note() << "Cycle()";
4✔
500
    SerialClient->Cycle();
4✔
501
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
502
    EXPECT_EQ(0xffff, Device->Registers[24]);
4✔
503
    EXPECT_EQ(0xff85, Device->Registers[25]);
4✔
504

505
    Note() << "server -> client: 0xffff 0xff85 (scaled)";
4✔
506
    Device->Registers[24] = 0xffff;
4✔
507
    Device->Registers[25] = 0xff85;
4✔
508
    Note() << "Cycle()";
4✔
509
    SerialClient->Cycle();
4✔
510
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
511
}
4✔
512

513
TEST_F(TSerialClientTest, S24)
16✔
514
{
515
    PRegister reg20 = Reg(20, S24);
8✔
516
    PRegister reg30 = Reg(30, S24);
8✔
517
    // create scaled register
518
    PRegister reg24 = Reg(24, S24, 0.001);
8✔
519

520
    SerialClient->AddDevice(Device);
4✔
521

522
    Note() << "server -> client: 10, 20";
4✔
523
    Device->Registers[20] = 0x002A;
4✔
524
    Device->Registers[21] = 0x00BB;
4✔
525
    Device->Registers[30] = 0x00FF;
4✔
526
    Device->Registers[31] = 0xFFFF;
4✔
527
    Note() << "Cycle()";
4✔
528
    SerialClient->Cycle();
4✔
529
    EXPECT_EQ(to_string(0x002A00BB), GetTextValue(reg20));
8✔
530
    EXPECT_EQ(to_string(-1), GetTextValue(reg30));
8✔
531

532
    Note() << "client -> server: 10";
4✔
533
    SerialClient->SetTextValue(reg20, "10");
4✔
534
    Note() << "Cycle()";
4✔
535
    SerialClient->Cycle();
4✔
536
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
537
    EXPECT_EQ(0, Device->Registers[20]);
4✔
538
    EXPECT_EQ(10, Device->Registers[21]);
4✔
539

540
    Note() << "client -> server: -2";
4✔
541
    SerialClient->SetTextValue(reg20, "-2");
4✔
542
    Note() << "Cycle()";
4✔
543
    SerialClient->Cycle();
4✔
544
    EXPECT_EQ(to_string(-2), GetTextValue(reg20));
8✔
545
    EXPECT_EQ(0x00FF, Device->Registers[20]);
4✔
546
    EXPECT_EQ(0xFFFE, Device->Registers[21]);
4✔
547

548
    Note() << "client -> server: -0.123 (scaled)";
4✔
549
    SerialClient->SetTextValue(reg24, "-0.123");
4✔
550
    Note() << "Cycle()";
4✔
551
    SerialClient->Cycle();
4✔
552
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
553
    EXPECT_EQ(0x00FF, Device->Registers[24]);
4✔
554
    EXPECT_EQ(0xFF85, Device->Registers[25]);
4✔
555

556
    Note() << "server -> client: 0x00ff 0xff85 (scaled)";
4✔
557
    Device->Registers[24] = 0x00FF;
4✔
558
    Device->Registers[25] = 0xFF85;
4✔
559
    Note() << "Cycle()";
4✔
560
    SerialClient->Cycle();
4✔
561
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
562
}
4✔
563

564
TEST_F(TSerialClientTest, U32)
16✔
565
{
566
    PRegister reg20 = Reg(20, U32);
8✔
567
    PRegister reg30 = Reg(30, U32);
8✔
568
    SerialClient->AddDevice(Device);
4✔
569

570
    Note() << "server -> client: 10, 20";
4✔
571
    Device->Registers[20] = 0x00AA;
4✔
572
    Device->Registers[21] = 0x00BB;
4✔
573
    Device->Registers[30] = 0xFFFF;
4✔
574
    Device->Registers[31] = 0xFFFF;
4✔
575
    Note() << "Cycle()";
4✔
576
    SerialClient->Cycle();
4✔
577
    EXPECT_EQ(to_string(0x00AA00BB), GetTextValue(reg20));
8✔
578
    EXPECT_EQ(to_string(0xFFFFFFFF), GetTextValue(reg30));
8✔
579

580
    Note() << "client -> server: 10";
4✔
581
    SerialClient->SetTextValue(reg20, "10");
4✔
582
    Note() << "Cycle()";
4✔
583
    SerialClient->Cycle();
4✔
584
    EXPECT_EQ(to_string(10), GetTextValue(reg20));
8✔
585
    EXPECT_EQ(0, Device->Registers[20]);
4✔
586
    EXPECT_EQ(10, Device->Registers[21]);
4✔
587

588
    Note() << "client -> server: -1 (overflow)";
4✔
589
    SerialClient->SetTextValue(reg20, "-1");
4✔
590
    Note() << "Cycle()";
4✔
591
    SerialClient->Cycle();
4✔
592
    EXPECT_EQ(to_string(0xFFFFFFFF), GetTextValue(reg20));
8✔
593
    EXPECT_EQ(0xFFFF, Device->Registers[20]);
4✔
594
    EXPECT_EQ(0xFFFF, Device->Registers[21]);
4✔
595

596
    Device->Registers[22] = 123;
4✔
597
    Device->Registers[23] = 123;
4✔
598
    Note() << "client -> server: 4294967296 (overflow)";
4✔
599
    SerialClient->SetTextValue(reg20, "4294967296");
4✔
600
    Note() << "Cycle()";
4✔
601
    SerialClient->Cycle();
4✔
602
    EXPECT_EQ(to_string(0), GetTextValue(reg20));
8✔
603
    EXPECT_EQ(0, Device->Registers[20]);
4✔
604
    EXPECT_EQ(0, Device->Registers[21]);
4✔
605

606
    // boundaries check
607
    EXPECT_EQ(123, Device->Registers[22]);
4✔
608
    EXPECT_EQ(123, Device->Registers[23]);
4✔
609
}
4✔
610

611
TEST_F(TSerialClientTest, BCD32)
16✔
612
{
613
    PRegister reg20 = Reg(20, BCD32);
8✔
614
    SerialClient->AddDevice(Device);
4✔
615
    Device->Registers[22] = 123;
4✔
616
    Device->Registers[23] = 123;
4✔
617

618
    Note() << "server -> client: 0x1234 0x5678";
4✔
619
    Device->Registers[20] = 0x1234;
4✔
620
    Device->Registers[21] = 0x5678;
4✔
621
    Note() << "Cycle()";
4✔
622
    SerialClient->Cycle();
4✔
623
    EXPECT_EQ(to_string(12345678), GetTextValue(reg20));
8✔
624

625
    Note() << "client -> server: 12345678";
4✔
626
    SerialClient->SetTextValue(reg20, "12345678");
4✔
627
    Note() << "Cycle()";
4✔
628
    SerialClient->Cycle();
4✔
629
    EXPECT_EQ(to_string(12345678), GetTextValue(reg20));
8✔
630
    EXPECT_EQ(0x1234, Device->Registers[20]);
4✔
631
    EXPECT_EQ(0x5678, Device->Registers[21]);
4✔
632

633
    Note() << "client -> server: 567890";
4✔
634
    SerialClient->SetTextValue(reg20, "567890");
4✔
635
    Note() << "Cycle()";
4✔
636
    SerialClient->Cycle();
4✔
637
    EXPECT_EQ(to_string(567890), GetTextValue(reg20));
8✔
638
    EXPECT_EQ(0x0056, Device->Registers[20]);
4✔
639
    EXPECT_EQ(0x7890, Device->Registers[21]);
4✔
640

641
    // boundaries check
642
    EXPECT_EQ(123, Device->Registers[22]);
4✔
643
    EXPECT_EQ(123, Device->Registers[23]);
4✔
644
}
4✔
645

646
TEST_F(TSerialClientTest, BCD24)
16✔
647
{
648
    PRegister reg20 = Reg(20, BCD24);
8✔
649
    SerialClient->AddDevice(Device);
4✔
650
    Device->Registers[22] = 123;
4✔
651
    Device->Registers[23] = 123;
4✔
652

653
    Note() << "server -> client: 0x0034 0x5678";
4✔
654
    Device->Registers[20] = 0x0034;
4✔
655
    Device->Registers[21] = 0x5678;
4✔
656
    Note() << "Cycle()";
4✔
657
    SerialClient->Cycle();
4✔
658
    EXPECT_EQ(to_string(345678), GetTextValue(reg20));
8✔
659

660
    Note() << "client -> server: 567890";
4✔
661
    SerialClient->SetTextValue(reg20, "567890");
4✔
662
    Note() << "Cycle()";
4✔
663
    SerialClient->Cycle();
4✔
664
    EXPECT_EQ(to_string(567890), GetTextValue(reg20));
8✔
665
    EXPECT_EQ(0x0056, Device->Registers[20]);
4✔
666
    EXPECT_EQ(0x7890, Device->Registers[21]);
4✔
667

668
    // boundaries check
669
    EXPECT_EQ(123, Device->Registers[22]);
4✔
670
    EXPECT_EQ(123, Device->Registers[23]);
4✔
671
}
4✔
672

673
TEST_F(TSerialClientTest, BCD16)
16✔
674
{
675
    PRegister reg20 = Reg(20, BCD16);
8✔
676
    SerialClient->AddDevice(Device);
4✔
677
    Device->Registers[21] = 123;
4✔
678

679
    Note() << "server -> client: 0x1234";
4✔
680
    Device->Registers[20] = 0x1234;
4✔
681
    Note() << "Cycle()";
4✔
682
    SerialClient->Cycle();
4✔
683
    EXPECT_EQ(to_string(1234), GetTextValue(reg20));
8✔
684

685
    Note() << "client -> server: 1234";
4✔
686
    SerialClient->SetTextValue(reg20, "1234");
4✔
687
    Note() << "Cycle()";
4✔
688
    SerialClient->Cycle();
4✔
689
    EXPECT_EQ(to_string(1234), GetTextValue(reg20));
8✔
690
    EXPECT_EQ(0x1234, Device->Registers[20]);
4✔
691

692
    // boundaries check
693
    EXPECT_EQ(123, Device->Registers[21]);
4✔
694
}
4✔
695

696
TEST_F(TSerialClientTest, BCD8)
16✔
697
{
698
    PRegister reg20 = Reg(20, BCD8);
8✔
699
    SerialClient->AddDevice(Device);
4✔
700
    Device->Registers[21] = 123;
4✔
701

702
    Note() << "server -> client: 0x12";
4✔
703
    Device->Registers[20] = 0x12;
4✔
704
    Note() << "Cycle()";
4✔
705
    SerialClient->Cycle();
4✔
706
    EXPECT_EQ(to_string(12), GetTextValue(reg20));
8✔
707

708
    Note() << "client -> server: 12";
4✔
709
    SerialClient->SetTextValue(reg20, "12");
4✔
710
    Note() << "Cycle()";
4✔
711
    SerialClient->Cycle();
4✔
712
    EXPECT_EQ(to_string(12), GetTextValue(reg20));
8✔
713
    EXPECT_EQ(0x12, Device->Registers[20]);
4✔
714

715
    // boundaries check
716
    EXPECT_EQ(123, Device->Registers[21]);
4✔
717
}
4✔
718

719
TEST_F(TSerialClientTest, Float32)
16✔
720
{
721
    // create scaled register
722
    PRegister reg24 = Reg(24, Float, 100);
8✔
723
    PRegister reg20 = Reg(20, Float);
8✔
724
    PRegister reg30 = Reg(30, Float);
8✔
725
    SerialClient->AddDevice(Device);
4✔
726

727
    Note() << "server -> client: 0x45d2 0x0000, 0x449d 0x8000";
4✔
728
    Device->Registers[20] = 0x45d2;
4✔
729
    Device->Registers[21] = 0x0000;
4✔
730
    Device->Registers[30] = 0x449d;
4✔
731
    Device->Registers[31] = 0x8000;
4✔
732
    Note() << "Cycle()";
4✔
733
    SerialClient->Cycle();
4✔
734
    EXPECT_EQ("6720", GetTextValue(reg20));
8✔
735
    EXPECT_EQ("1260", GetTextValue(reg30));
8✔
736

737
    Note() << "client -> server: 10";
4✔
738
    SerialClient->SetTextValue(reg20, "10");
4✔
739
    Note() << "Cycle()";
4✔
740
    SerialClient->Cycle();
4✔
741
    EXPECT_EQ("10", GetTextValue(reg20));
8✔
742
    EXPECT_EQ(0x4120, Device->Registers[20]);
4✔
743
    EXPECT_EQ(0x0000, Device->Registers[21]);
4✔
744

745
    Note() << "client -> server: -0.00123";
4✔
746
    SerialClient->SetTextValue(reg20, "-0.00123");
4✔
747
    Note() << "Cycle()";
4✔
748
    SerialClient->Cycle();
4✔
749
    EXPECT_EQ("-0.00123", GetTextValue(reg20));
8✔
750
    EXPECT_EQ(0xbaa1, Device->Registers[20]);
4✔
751
    EXPECT_EQ(0x37f4, Device->Registers[21]);
4✔
752

753
    Note() << "client -> server: -0.123 (scaled)";
4✔
754
    SerialClient->SetTextValue(reg24, "-0.123");
4✔
755
    Note() << "Cycle()";
4✔
756
    SerialClient->Cycle();
4✔
757
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
758
    EXPECT_EQ(0xbaa1, Device->Registers[24]);
4✔
759
    EXPECT_EQ(0x37f4, Device->Registers[25]);
4✔
760

761
    Note() << "server -> client: 0x449d 0x8000 (scaled)";
4✔
762
    Device->Registers[24] = 0x449d;
4✔
763
    Device->Registers[25] = 0x8000;
4✔
764
    Note() << "Cycle()";
4✔
765
    SerialClient->Cycle();
4✔
766
    EXPECT_EQ("126000", GetTextValue(reg24));
8✔
767
}
4✔
768

769
TEST_F(TSerialClientTest, Double64)
16✔
770
{
771
    // create scaled register
772
    PRegister reg24 = Reg(24, Double, 100);
8✔
773
    PRegister reg20 = Reg(20, Double);
8✔
774
    PRegister reg30 = Reg(30, Double);
8✔
775
    SerialClient->AddDevice(Device);
4✔
776

777
    Note() << "server -> client: 40ba401f7ced9168 , 4093b148b4395810";
4✔
778
    Device->Registers[20] = 0x40ba;
4✔
779
    Device->Registers[21] = 0x401f;
4✔
780
    Device->Registers[22] = 0x7ced;
4✔
781
    Device->Registers[23] = 0x9168;
4✔
782

783
    Device->Registers[30] = 0x4093;
4✔
784
    Device->Registers[31] = 0xb148;
4✔
785
    Device->Registers[32] = 0xb439;
4✔
786
    Device->Registers[33] = 0x5810;
4✔
787

788
    Note() << "Cycle()";
4✔
789
    SerialClient->Cycle();
4✔
790
    EXPECT_EQ("6720.123", GetTextValue(reg20));
8✔
791
    EXPECT_EQ("1260.321", GetTextValue(reg30));
8✔
792

793
    Note() << "client -> server: 10";
4✔
794
    SerialClient->SetTextValue(reg20, "10.9999");
4✔
795
    Note() << "Cycle()";
4✔
796
    SerialClient->Cycle();
4✔
797
    EXPECT_EQ("10.9999", GetTextValue(reg20));
8✔
798
    EXPECT_EQ(0x4025, Device->Registers[20]);
4✔
799
    EXPECT_EQ(0xfff2, Device->Registers[21]);
4✔
800
    EXPECT_EQ(0xe48e, Device->Registers[22]);
4✔
801
    EXPECT_EQ(0x8a72, Device->Registers[23]);
4✔
802

803
    Note() << "client -> server: -0.00123";
4✔
804
    SerialClient->SetTextValue(reg20, "-0.00123");
4✔
805
    Note() << "Cycle()";
4✔
806
    SerialClient->Cycle();
4✔
807
    EXPECT_EQ("-0.00123", GetTextValue(reg20));
8✔
808
    EXPECT_EQ(0xbf54, Device->Registers[20]);
4✔
809
    EXPECT_EQ(0x26fe, Device->Registers[21]);
4✔
810
    EXPECT_EQ(0x718a, Device->Registers[22]);
4✔
811
    EXPECT_EQ(0x86d7, Device->Registers[23]);
4✔
812

813
    Note() << "client -> server: -0.123 (scaled)";
4✔
814
    SerialClient->SetTextValue(reg24, "-0.123");
4✔
815
    Note() << "Cycle()";
4✔
816
    SerialClient->Cycle();
4✔
817
    EXPECT_EQ("-0.123", GetTextValue(reg24));
8✔
818
    EXPECT_EQ(0xbf54, Device->Registers[24]);
4✔
819
    EXPECT_EQ(0x26fe, Device->Registers[25]);
4✔
820
    EXPECT_EQ(0x718a, Device->Registers[26]);
4✔
821
    EXPECT_EQ(0x86d7, Device->Registers[27]);
4✔
822

823
    Note() << "server -> client: 4093b00000000000 (scaled)";
4✔
824
    Device->Registers[24] = 0x4093;
4✔
825
    Device->Registers[25] = 0xb000;
4✔
826
    Device->Registers[26] = 0x0000;
4✔
827
    Device->Registers[27] = 0x0000;
4✔
828

829
    Note() << "Cycle()";
4✔
830
    SerialClient->Cycle();
4✔
831
    EXPECT_EQ("126000", GetTextValue(reg24));
8✔
832
}
4✔
833

834
TEST_F(TSerialClientTest, String)
16✔
835
{
836
    PRegister reg20 = Reg(20, String, 1, 0, 0, EWordOrder::BigEndian, EByteOrder::BigEndian, 0, 32 * 8);
8✔
837
    PRegister reg32 = Reg(62, String, 1, 0, 0, EWordOrder::BigEndian, EByteOrder::BigEndian, 0, 32 * 8);
8✔
838
    SerialClient->AddDevice(Device);
4✔
839

840
    Note() << "server -> client: 40ba401f7ced9168 , 4093b148b4395810";
4✔
841
    Device->Registers[20] = 'H';
4✔
842
    Device->Registers[21] = 'e';
4✔
843
    Device->Registers[22] = 'l';
4✔
844
    Device->Registers[23] = 'l';
4✔
845
    Device->Registers[24] = 'o';
4✔
846
    Device->Registers[25] = ',';
4✔
847
    Device->Registers[26] = ' ';
4✔
848
    Device->Registers[27] = 'w';
4✔
849
    Device->Registers[28] = 'o';
4✔
850
    Device->Registers[29] = 'r';
4✔
851
    Device->Registers[30] = 'l';
4✔
852
    Device->Registers[31] = 'd';
4✔
853
    Device->Registers[32] = '!';
4✔
854

855
    Device->Registers[62] = 'a';
4✔
856
    Device->Registers[63] = 'b';
4✔
857
    Device->Registers[64] = 'c';
4✔
858
    Device->Registers[65] = 'd';
4✔
859

860
    Note() << "Cycle()";
4✔
861
    SerialClient->Cycle();
4✔
862
    EXPECT_EQ(std::string("Hello, world!"), GetTextValue(reg20));
8✔
863
    EXPECT_EQ("abcd", GetTextValue(reg32));
8✔
864

865
    Note() << "client -> server: 10";
4✔
866
    SerialClient->SetTextValue(reg20, "computer");
4✔
867
    Note() << "Cycle()";
4✔
868
    SerialClient->Cycle();
4✔
869
    EXPECT_EQ("computer", GetTextValue(reg20));
8✔
870
    EXPECT_EQ('c', static_cast<char>(Device->Registers[20]));
4✔
871
    EXPECT_EQ('o', static_cast<char>(Device->Registers[21]));
4✔
872
    EXPECT_EQ('m', static_cast<char>(Device->Registers[22]));
4✔
873
    EXPECT_EQ('p', static_cast<char>(Device->Registers[23]));
4✔
874
    EXPECT_EQ('u', static_cast<char>(Device->Registers[24]));
4✔
875
    EXPECT_EQ('t', static_cast<char>(Device->Registers[25]));
4✔
876
    EXPECT_EQ('e', static_cast<char>(Device->Registers[26]));
4✔
877
    EXPECT_EQ('r', static_cast<char>(Device->Registers[27]));
4✔
878
    EXPECT_EQ('\0', static_cast<char>(Device->Registers[28]));
4✔
879
}
4✔
880

881
TEST_F(TSerialClientTest, offset)
16✔
882
{
883
    // create scaled register with offset
884
    PRegister reg24 = Reg(24, S16, 3, -15);
8✔
885
    SerialClient->AddDevice(Device);
4✔
886

887
    Note() << "client -> server: -87 (scaled)";
4✔
888
    SerialClient->SetTextValue(reg24, "-87");
4✔
889
    Note() << "Cycle()";
4✔
890
    SerialClient->Cycle();
4✔
891
    EXPECT_EQ("-87", GetTextValue(reg24));
8✔
892
    EXPECT_EQ(0xffe8, Device->Registers[24]);
4✔
893
}
4✔
894

895
TEST_F(TSerialClientTest, Round)
16✔
896
{
897
    PRegister reg24_0_01 = Reg(24, Float, 1, 0, 0.01);
4✔
898
    PRegister reg26_1 = Reg(26, Float, 1, 0, 1);
4✔
899
    PRegister reg28_10 = Reg(28, Float, 1, 0, 10);
4✔
900
    PRegister reg30_0_2 = Reg(30, Float, 1, 0, 0.2);
4✔
901
    PRegister reg32_0_01 = Reg(32, Float, 1, 0, 0.01);
4✔
902

903
    SerialClient->AddDevice(Device);
4✔
904

905
    Note() << "client -> server: 12.345 (not rounded)";
4✔
906
    SerialClient->SetTextValue(reg24_0_01, "12.345");
4✔
907
    SerialClient->SetTextValue(reg26_1, "12.345");
4✔
908
    SerialClient->SetTextValue(reg28_10, "12.345");
4✔
909
    SerialClient->SetTextValue(reg30_0_2, "12.345");
4✔
910
    SerialClient->SetTextValue(reg32_0_01, "12.344");
4✔
911
    Note() << "Cycle()";
4✔
912
    SerialClient->Cycle();
4✔
913
    EXPECT_EQ("12.35", GetTextValue(reg24_0_01));
8✔
914
    EXPECT_EQ("12", GetTextValue(reg26_1));
8✔
915
    EXPECT_EQ("10", GetTextValue(reg28_10));
8✔
916
    EXPECT_EQ("12.4", GetTextValue(reg30_0_2));
8✔
917
    EXPECT_EQ("12.34", GetTextValue(reg32_0_01));
8✔
918

919
    union
920
    {
921
        uint32_t words;
922
        float value;
923
    } data;
924

925
    data.words = Device->Read2Registers(24);
4✔
926
    ASSERT_EQ(12.35f, data.value);
4✔
927

928
    data.words = Device->Read2Registers(26);
4✔
929
    ASSERT_EQ(12.f, data.value);
4✔
930

931
    data.words = Device->Read2Registers(28);
4✔
932
    ASSERT_EQ(10.f, data.value);
4✔
933

934
    data.words = Device->Read2Registers(30);
4✔
935
    ASSERT_EQ(12.4f, data.value);
4✔
936

937
    data.words = Device->Read2Registers(32);
4✔
938
    ASSERT_EQ(12.34f, data.value);
4✔
939
}
940

941
TEST_F(TSerialClientTest, Errors)
16✔
942
{
943
    PRegister reg20 = Reg(20);
8✔
944
    SerialClient->AddDevice(Device);
4✔
945

946
    Note() << "Cycle() [first start]";
4✔
947
    SerialClient->Cycle();
4✔
948

949
    Device->BlockReadFor(20, true);
4✔
950
    Device->BlockWriteFor(20, true);
4✔
951

952
    for (int i = 0; i < 3; i++) {
16✔
953
        Note() << "Cycle() [read, rw blacklisted]";
12✔
954
        SerialClient->Cycle();
12✔
955
    }
956

957
    SerialClient->SetTextValue(reg20, "42");
4✔
958
    Note() << "Cycle() [write, rw blacklisted]";
4✔
959
    SerialClient->Cycle();
4✔
960

961
    Device->BlockWriteFor(20, false);
4✔
962
    SerialClient->SetTextValue(reg20, "42");
4✔
963
    Note() << "Cycle() [write, r blacklisted]";
4✔
964
    SerialClient->Cycle();
4✔
965

966
    Device->BlockWriteFor(20, true);
4✔
967
    SerialClient->SetTextValue(reg20, "43");
4✔
968
    Note() << "Cycle() [write, rw blacklisted]";
4✔
969
    SerialClient->Cycle();
4✔
970

971
    Device->BlockReadFor(20, false);
4✔
972
    Note() << "Cycle() [write, w blacklisted]";
4✔
973
    SerialClient->Cycle();
4✔
974

975
    Device->BlockWriteFor(20, false);
4✔
976
    SerialClient->SetTextValue(reg20, "42");
4✔
977
    Note() << "Cycle() [write, nothing blacklisted]";
4✔
978
    SerialClient->Cycle();
4✔
979
    reg20->GetConfig()->ErrorValue = TRegisterValue{42};
4✔
980
    Note() << "Cycle() [read, set error value for register]";
4✔
981
    SerialClient->Cycle();
4✔
982

983
    SerialClient->Cycle();
4✔
984
}
4✔
985

986
TEST_F(TSerialClientTestWithSetupRegisters, SetupOk)
16✔
987
{
988
    PRegister reg20 = Reg(20);
8✔
989
    SerialClient->AddDevice(Device);
4✔
990
    SerialClient->Cycle();
4✔
991
    EXPECT_EQ(Device->GetConnectionState(), TDeviceConnectionState::CONNECTED);
4✔
992
}
4✔
993

994
TEST_F(TSerialClientTestWithSetupRegisters, SetupFail)
16✔
995
{
996
    PRegister reg20 = Reg(20);
8✔
997
    SerialClient->AddDevice(Device);
4✔
998
    Device->BlockWriteFor(101, true);
4✔
999
    SerialClient->Cycle();
4✔
1000
    EXPECT_EQ(Device->GetConnectionState(), TDeviceConnectionState::DISCONNECTED);
4✔
1001
    Device->BlockWriteFor(101, false);
4✔
1002
    SerialClient->Cycle();
4✔
1003
    EXPECT_EQ(Device->GetConnectionState(), TDeviceConnectionState::CONNECTED);
4✔
1004
}
4✔
1005

1006
class TSerialClientIntegrationTest: public TSerialClientTest
1007
{
1008
protected:
1009
    void SetUp();
1010
    void TearDown();
1011
    void FilterConfig(const std::string& device_name);
1012
    void Publish(const std::string& topic, const std::string& payload, uint8_t qos = 0, bool retain = true);
1013
    void PublishWaitOnValue(const std::string& topic, const std::string& payload, uint8_t qos = 0, bool retain = true);
1014

1015
    void FixFakeDevices(PHandlerConfig config);
1016

1017
    /** reconnect test functions **/
1018
    static void DeviceTimeoutOnly(const PSerialDevice& device, chrono::milliseconds timeout);
1019
    static void DeviceMaxFailCyclesOnly(const PSerialDevice& device, int cycleCount);
1020
    static void DeviceTimeoutAndMaxFailCycles(const PSerialDevice& device,
1021
                                              chrono::milliseconds timeout,
1022
                                              int cycleCount);
1023

1024
    PMQTTSerialDriver StartReconnectTest1Device(bool miss = false, bool pollIntervalTest = false);
1025
    PMQTTSerialDriver StartReconnectTest2Devices();
1026

1027
    void ReconnectTest1Device(function<void()>&& thunk, bool pollIntervalTest = false);
1028
    void ReconnectTest2Devices(function<void()>&& thunk);
1029

1030
    /**rpc request test functions**/
1031
    TRPCResultCode SendRPCRequest(PMQTTSerialDriver serialDriver,
1032
                                  std::vector<int> expectedRequest,
1033
                                  std::vector<int> expectedResponse,
1034
                                  size_t expectedResponseLength,
1035
                                  std::chrono::seconds totalTimeout);
1036

1037
    PFakeMqttBroker MqttBroker;
1038
    PFakeMqttClient MqttClient;
1039
    PDeviceDriver Driver;
1040
    PMQTTSerialDriver SerialDriver;
1041
    PHandlerConfig Config;
1042
    Json::Value CommonDeviceSchema;
1043
    Json::Value PortsSchema;
1044
    std::shared_ptr<TProtocolConfedSchemasMap> ProtocolSchemas;
1045

1046
    static const char* const Name;
1047
    static const char* const OtherName;
1048
};
1049

1050
const char* const TSerialClientIntegrationTest::Name = "serial-client-integration-test";
1051
const char* const TSerialClientIntegrationTest::OtherName = "serial-client-integration-test-other";
1052

1053
void TSerialClientIntegrationTest::SetUp()
38✔
1054
{
1055
    SetMode(E_Unordered);
38✔
1056
    TSerialClientTest::SetUp();
38✔
1057
    std::filesystem::remove(DB_PATH);
38✔
1058

1059
    MqttBroker = NewFakeMqttBroker(*this);
38✔
1060
    MqttClient = MqttBroker->MakeClient(Name);
38✔
1061
    auto backend = NewDriverBackend(MqttClient);
76✔
1062
    Driver = NewDriver(TDriverArgs{}
114✔
1063
                           .SetId(Name)
76✔
1064
                           .SetBackend(backend)
38✔
1065
                           .SetIsTesting(true)
38✔
1066
                           .SetReownUnknownDevices(true)
38✔
1067
                           .SetUseStorage(true)
38✔
1068
                           .SetStoragePath(DB_PATH));
76✔
1069

1070
    Driver->StartLoop();
38✔
1071

1072
    CommonDeviceSchema = WBMQTT::JSON::Parse(GetDataFilePath("../wb-mqtt-serial-confed-common.schema.json"));
38✔
1073
    PortsSchema = WBMQTT::JSON::Parse(GetDataFilePath("../wb-mqtt-serial-ports.schema.json"));
38✔
1074
    ProtocolSchemas = std::make_shared<TProtocolConfedSchemasMap>(GetDataFilePath("../protocols"), CommonDeviceSchema);
38✔
1075
    ProtocolSchemas->AddFolder(GetDataFilePath("protocols"));
38✔
1076
    AddRegisterType(CommonDeviceSchema, "fake");
38✔
1077
    TTemplateMap t;
38✔
1078

1079
    Config = LoadConfig(
38✔
1080
        GetDataFilePath("configs/config-test.json"),
76✔
1081
        DeviceFactory,
38✔
1082
        CommonDeviceSchema,
38✔
1083
        t,
1084
        rpcConfig,
38✔
1085
        PortsSchema,
38✔
1086
        *ProtocolSchemas,
38✔
1087
        [=](const Json::Value&, PRPCConfig rpcConfig) { return std::make_shared<TFeaturePort>(Port, false); });
190✔
1088

1089
    FixFakeDevices(Config);
38✔
1090
}
38✔
1091

1092
void TSerialClientIntegrationTest::TearDown()
38✔
1093
{
1094
    if (SerialDriver) {
38✔
1095
        SerialDriver->ClearDevices();
18✔
1096
    }
1097
    Driver->StopLoop();
38✔
1098
    TSerialClientTest::TearDown();
38✔
1099
    std::filesystem::remove(DB_PATH);
38✔
1100
}
38✔
1101

1102
void TSerialClientIntegrationTest::FixFakeDevices(PHandlerConfig config)
58✔
1103
{
1104
    for (auto port_config: config->PortConfigs) {
116✔
1105
        PFakeSerialPort fakePort = std::dynamic_pointer_cast<TFakeSerialPort>(port_config->Port);
116✔
1106
        if (!fakePort) {
58✔
1107
            auto feature = std::dynamic_pointer_cast<TFeaturePort>(port_config->Port);
58✔
1108
            fakePort = std::dynamic_pointer_cast<TFakeSerialPort>(feature->GetBasePort());
58✔
1109
        }
1110
        if (fakePort) {
58✔
1111
            for (auto device: port_config->Devices) {
270✔
1112
                PFakeSerialDevice fakeDevice = std::dynamic_pointer_cast<TFakeSerialDevice>(device->Device);
424✔
1113
                if (fakeDevice) {
212✔
1114
                    fakeDevice->SetFakePort(fakePort);
208✔
1115
                }
1116
            }
1117
        }
1118
    }
1119
}
58✔
1120

1121
void TSerialClientIntegrationTest::FilterConfig(const std::string& device_name)
18✔
1122
{
1123
    for (auto port_config: Config->PortConfigs) {
36✔
1124
        LOG(Info) << "port devices: " << port_config->Devices.size();
18✔
1125
        port_config->Devices.erase(
18✔
1126
            remove_if(port_config->Devices.begin(),
×
1127
                      port_config->Devices.end(),
18✔
1128
                      [device_name](auto device) { return device->Device->DeviceConfig()->Name != device_name; }),
136✔
1129
            port_config->Devices.end());
36✔
1130
    }
1131
    Config->PortConfigs.erase(remove_if(Config->PortConfigs.begin(),
18✔
1132
                                        Config->PortConfigs.end(),
18✔
1133
                                        [](PPortConfig port_config) { return port_config->Devices.empty(); }),
54✔
1134
                              Config->PortConfigs.end());
36✔
1135
    ASSERT_FALSE(Config->PortConfigs.empty()) << "device not found: " << device_name;
18✔
1136
}
1137

1138
void TSerialClientIntegrationTest::DeviceTimeoutOnly(const PSerialDevice& device, chrono::milliseconds timeout)
6✔
1139
{
1140
    device->DeviceConfig()->DeviceTimeout = timeout;
6✔
1141
    device->DeviceConfig()->DeviceMaxFailCycles = 0;
6✔
1142
}
6✔
1143

1144
void TSerialClientIntegrationTest::DeviceMaxFailCyclesOnly(const PSerialDevice& device, int cycleCount)
2✔
1145
{
1146
    device->DeviceConfig()->DeviceTimeout = chrono::milliseconds(0);
2✔
1147
    device->DeviceConfig()->DeviceMaxFailCycles = cycleCount;
2✔
1148
}
2✔
1149

1150
void TSerialClientIntegrationTest::DeviceTimeoutAndMaxFailCycles(const PSerialDevice& device,
2✔
1151
                                                                 chrono::milliseconds timeout,
1152
                                                                 int cycleCount)
1153
{
1154
    device->DeviceConfig()->DeviceTimeout = timeout;
2✔
1155
    device->DeviceConfig()->DeviceMaxFailCycles = cycleCount;
2✔
1156
}
2✔
1157

1158
void TSerialClientIntegrationTest::Publish(const std::string& topic,
48✔
1159
                                           const std::string& payload,
1160
                                           uint8_t qos,
1161
                                           bool retain)
1162
{
1163
    MqttBroker->Publish("em-test-other", {TMqttMessage{topic, payload, qos, retain}});
96✔
1164
}
48✔
1165

1166
void TSerialClientIntegrationTest::PublishWaitOnValue(const std::string& topic,
48✔
1167
                                                      const std::string& payload,
1168
                                                      uint8_t qos,
1169
                                                      bool retain)
1170
{
1171
    auto done = std::make_shared<WBMQTT::TPromise<void>>();
48✔
1172
    Driver->On<WBMQTT::TControlOnValueEvent>([=](const WBMQTT::TControlOnValueEvent& event) {
96✔
1173
        if (!done->IsFulfilled()) {
120✔
1174
            done->Complete();
48✔
1175
        }
1176
    });
96✔
1177
    Publish(topic, payload, qos, retain);
48✔
1178
    done->GetFuture().Sync();
48✔
1179
}
48✔
1180

1181
TEST_F(TSerialClientIntegrationTest, OnValue)
16✔
1182
{
1183
    FilterConfig("OnValueTest");
4✔
1184

1185
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1186

1187
    auto device = TFakeSerialDevice::GetDevice("0x90");
4✔
1188

1189
    if (!device) {
4✔
1190
        throw std::runtime_error("device not found or wrong type");
×
1191
    }
1192

1193
    device->Registers[0] = 0;
4✔
1194
    device->Registers[1] = 0;
4✔
1195
    device->Registers[2] = 0;
4✔
1196
    device->Registers[3] = 0;
4✔
1197

1198
    Note() << "LoopOnce()";
4✔
1199
    SerialDriver->LoopOnce();
4✔
1200

1201
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 1/on", "1", 0, true);
4✔
1202
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 2/on", "1", 0, true);
4✔
1203
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 3/on", "1", 0, true);
4✔
1204

1205
    Note() << "LoopOnce()";
4✔
1206
    SerialDriver->LoopOnce();
4✔
1207

1208
    ASSERT_EQ(500, device->Registers[0]);
4✔
1209
    ASSERT_EQ(-1, device->Registers[1] << 16 | device->Registers[2]);
4✔
1210
    ASSERT_EQ(-1, static_cast<int16_t>(device->Registers[3]));
4✔
1211
}
1212

1213
TEST_F(TSerialClientIntegrationTest, OffValue)
16✔
1214
{
1215
    FilterConfig("OnValueTest");
4✔
1216

1217
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1218

1219
    auto device = TFakeSerialDevice::GetDevice("0x90");
4✔
1220

1221
    if (!device) {
4✔
1222
        throw std::runtime_error("device not found or wrong type");
×
1223
    }
1224

1225
    device->Registers[0] = 500;
4✔
1226
    device->Registers[1] = 0xFFFF;
4✔
1227
    device->Registers[2] = 0xFFFF;
4✔
1228
    device->Registers[3] = 0xFFFF;
4✔
1229

1230
    Note() << "LoopOnce()";
4✔
1231
    SerialDriver->LoopOnce();
4✔
1232

1233
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 1/on", "0", 0, true);
4✔
1234
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 2/on", "0", 0, true);
4✔
1235
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 3/on", "0", 0, true);
4✔
1236

1237
    Note() << "LoopOnce()";
4✔
1238
    SerialDriver->LoopOnce();
4✔
1239

1240
    ASSERT_EQ(200, device->Registers[0]);
4✔
1241
    ASSERT_EQ(-2, device->Registers[1] << 16 | device->Registers[2]);
4✔
1242
    ASSERT_EQ(-2, static_cast<int16_t>(device->Registers[3]));
4✔
1243
}
1244

1245
TEST_F(TSerialClientIntegrationTest, OnValueError)
16✔
1246
{
1247
    FilterConfig("OnValueTest");
4✔
1248

1249
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1250

1251
    auto device = TFakeSerialDevice::GetDevice("0x90");
4✔
1252

1253
    if (!device) {
4✔
1254
        throw std::runtime_error("device not found or wrong type");
×
1255
    }
1256

1257
    device->Registers[0] = 0;
4✔
1258
    device->Registers[1] = 0;
4✔
1259
    device->Registers[2] = 0;
4✔
1260
    device->Registers[4] = 0;
4✔
1261

1262
    Note() << "LoopOnce()";
4✔
1263
    SerialDriver->LoopOnce();
4✔
1264

1265
    device->BlockWriteFor(0, true);
4✔
1266

1267
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 1/on", "1", 0, true);
4✔
1268
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 2/on", "1", 0, true);
4✔
1269
    PublishWaitOnValue("/devices/OnValueTest/controls/Relay 3/on", "1", 0, true);
4✔
1270

1271
    Note() << "LoopOnce()";
4✔
1272
    SerialDriver->LoopOnce();
4✔
1273

1274
    Note() << "LoopOnce()";
4✔
1275
    SerialDriver->LoopOnce();
4✔
1276

1277
    device->BlockWriteFor(0, false);
4✔
1278

1279
    Note() << "LoopOnce()";
4✔
1280
    SerialDriver->LoopOnce();
4✔
1281

1282
    ASSERT_EQ(500, device->Registers[0]);
4✔
1283
    ASSERT_EQ(-1, device->Registers[1] << 16 | device->Registers[2]);
4✔
1284
    ASSERT_EQ(-1, static_cast<int16_t>(device->Registers[3]));
4✔
1285
}
1286

1287
TEST_F(TSerialClientIntegrationTest, Round)
16✔
1288
{
1289
    FilterConfig("RoundTest");
4✔
1290

1291
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1292

1293
    auto device = TFakeSerialDevice::GetDevice("0x92");
4✔
1294

1295
    if (!device) {
4✔
1296
        throw std::runtime_error("device not found or wrong type");
×
1297
    }
1298

1299
    device->Registers[0] = 0;
4✔
1300
    Note() << "LoopOnce()";
4✔
1301
    SerialDriver->LoopOnce();
4✔
1302

1303
    PublishWaitOnValue("/devices/RoundTest/controls/Float_0_01/on", "12.345", 0, true);
4✔
1304
    PublishWaitOnValue("/devices/RoundTest/controls/Float_1/on", "12.345", 0, true);
4✔
1305
    PublishWaitOnValue("/devices/RoundTest/controls/Float_10/on", "12.345", 0, true);
4✔
1306
    PublishWaitOnValue("/devices/RoundTest/controls/Float_0_2/on", "12.345", 0, true);
4✔
1307

1308
    Note() << "LoopOnce()";
4✔
1309
    SerialDriver->LoopOnce();
4✔
1310

1311
    union
1312
    {
1313
        uint32_t words;
1314
        float value;
1315
    } data;
1316

1317
    data.words = device->Read2Registers(0);
4✔
1318
    ASSERT_EQ(12.35f, data.value);
4✔
1319

1320
    data.words = device->Read2Registers(2);
4✔
1321
    ASSERT_EQ(12.f, data.value);
4✔
1322

1323
    data.words = device->Read2Registers(4);
4✔
1324
    ASSERT_EQ(10.f, data.value);
4✔
1325

1326
    data.words = device->Read2Registers(6);
4✔
1327
    ASSERT_EQ(12.4f, data.value);
4✔
1328
}
1329

1330
TEST_F(TSerialClientIntegrationTest, Errors)
16✔
1331
{
1332

1333
    FilterConfig("DDL24");
4✔
1334

1335
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1336

1337
    auto device = TFakeSerialDevice::GetDevice("23");
4✔
1338

1339
    if (!device) {
4✔
1340
        throw std::runtime_error("device not found or wrong type");
×
1341
    }
1342

1343
    Note() << "LoopOnce() [first start]";
4✔
1344
    SerialDriver->LoopOnce();
4✔
1345

1346
    device->BlockReadFor(4, true);
4✔
1347
    device->BlockWriteFor(4, true);
4✔
1348
    device->BlockReadFor(7, true);
4✔
1349
    device->BlockWriteFor(7, true);
4✔
1350

1351
    Note() << "LoopOnce() [read, rw blacklisted]";
4✔
1352
    SerialDriver->LoopOnce();
4✔
1353

1354
    PublishWaitOnValue("/devices/ddl24/controls/RGB/on", "10;20;30", 0, true);
4✔
1355
    PublishWaitOnValue("/devices/ddl24/controls/White/on", "42", 0, true);
4✔
1356

1357
    Note() << "LoopOnce() [write, rw blacklisted]";
4✔
1358
    SerialDriver->LoopOnce();
4✔
1359

1360
    device->BlockReadFor(4, false);
4✔
1361
    device->BlockWriteFor(4, false);
4✔
1362
    device->BlockReadFor(7, false);
4✔
1363
    device->BlockWriteFor(7, false);
4✔
1364

1365
    Note() << "LoopOnce() [read, nothing blacklisted]";
4✔
1366
    SerialDriver->LoopOnce();
4✔
1367

1368
    PublishWaitOnValue("/devices/ddl24/controls/RGB/on", "10;20;30", 0, true);
4✔
1369
    PublishWaitOnValue("/devices/ddl24/controls/White/on", "42", 0, true);
4✔
1370

1371
    Note() << "LoopOnce() [write, nothing blacklisted]";
4✔
1372
    SerialDriver->LoopOnce();
4✔
1373

1374
    Note() << "LoopOnce() [read, nothing blacklisted] (2)";
4✔
1375
    SerialDriver->LoopOnce();
4✔
1376
}
4✔
1377

1378
TEST_F(TSerialClientIntegrationTest, PollIntervalMissErrors)
16✔
1379
{
1380
    FilterConfig("PollIntervalMissError");
4✔
1381

1382
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1383

1384
    auto device = TFakeSerialDevice::GetDevice("0x97");
4✔
1385

1386
    if (!device) {
4✔
1387
        throw std::runtime_error("device not found or wrong type");
×
1388
    }
1389

1390
    Note() << "LoopOnce() [first start]";
4✔
1391
    SerialDriver->LoopOnce();
4✔
1392

1393
    device->Registers[0] = 0xFF;
4✔
1394

1395
    // TODO: https://wirenboard.bitrix24.ru/company/personal/user/134/tasks/task/view/46912/
1396
    for (size_t i = 0; i < 5; ++i) {
24✔
1397
        this_thread::sleep_for(2s);
20✔
1398

1399
        Note() << "LoopOnce() [interval miss]";
20✔
1400
        SerialDriver->LoopOnce();
20✔
1401
    }
1402

1403
    for (size_t i = 0; i < 10; ++i) {
44✔
1404
        this_thread::sleep_for(1s);
40✔
1405

1406
        Note() << "LoopOnce() [interval ok]";
40✔
1407
        SerialDriver->LoopOnce();
40✔
1408
    }
1409
}
4✔
1410

1411
TEST_F(TSerialClientIntegrationTest, SetupErrors)
16✔
1412
{
1413
    FilterConfig("DDL24");
4✔
1414

1415
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1416

1417
    auto device = TFakeSerialDevice::GetDevice("23");
4✔
1418

1419
    if (!device) {
4✔
1420
        throw std::runtime_error("device not found or wrong type");
×
1421
    }
1422

1423
    device->BlockWriteFor(1, true);
4✔
1424

1425
    Note() << "LoopOnce() [first start, write blacklisted]";
4✔
1426
    SerialDriver->LoopOnce();
4✔
1427

1428
    device->BlockWriteFor(1, false);
4✔
1429
    device->BlockWriteFor(2, true);
4✔
1430

1431
    Note() << "LoopOnce() [write blacklisted]";
4✔
1432
    SerialDriver->LoopOnce();
4✔
1433

1434
    device->BlockWriteFor(2, false);
4✔
1435

1436
    // Disconnected device poll delayed for 500ms on every poll retry
1437
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
4✔
1438

1439
    Note() << "LoopOnce()";
4✔
1440
    SerialDriver->LoopOnce();
4✔
1441
}
4✔
1442

1443
TEST_F(TSerialClientIntegrationTest, ErrorValue)
16✔
1444
{
1445
    FilterConfig("ErrorValueTest");
4✔
1446

1447
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1448

1449
    auto device = TFakeSerialDevice::GetDevice("0x96");
4✔
1450

1451
    if (!device) {
4✔
1452
        throw std::runtime_error("device not found or wrong type");
×
1453
    }
1454

1455
    Note() << "LoopOnce()";
4✔
1456
    SerialDriver->LoopOnce();
4✔
1457

1458
    device->Registers[0] = 0x7FFF;
4✔
1459
    device->Registers[1] = 0x7FFF;
4✔
1460
    device->Registers[2] = 0x7F;
4✔
1461
    device->Registers[3] = 0x7F;
4✔
1462
    device->Registers[4] = 0x7F;
4✔
1463
    device->Registers[5] = 0xFFFF;
4✔
1464
    device->Registers[6] = 0x7F;
4✔
1465
    device->Registers[7] = 0xFFFF;
4✔
1466
    device->Registers[8] = 0x7FFF;
4✔
1467
    device->Registers[9] = 0xFFFF;
4✔
1468
    device->Registers[10] = 0x7FFF;
4✔
1469
    device->Registers[11] = 0xFFFF;
4✔
1470
    device->Registers[12] = 0x7FFF;
4✔
1471
    device->Registers[13] = 0xFFFF;
4✔
1472
    device->Registers[14] = 0xFFFF;
4✔
1473
    device->Registers[15] = 0xFFFF;
4✔
1474
    device->Registers[16] = 0x7FFF;
4✔
1475
    device->Registers[17] = 0xFFFF;
4✔
1476
    device->Registers[18] = 0xFFFF;
4✔
1477
    device->Registers[19] = 0xFFFF;
4✔
1478
    device->Registers[20] = 0x7FFF;
4✔
1479
    device->Registers[21] = 0xFFFF;
4✔
1480
    device->Registers[22] = 0xFFFF;
4✔
1481
    device->Registers[23] = 0xFFFF;
4✔
1482
    device->Registers[24] = 0x7FFF;
4✔
1483
    device->Registers[25] = 0xFFFF;
4✔
1484

1485
    Note() << "LoopOnce() [all errors]";
4✔
1486
    SerialDriver->LoopOnce();
4✔
1487
}
4✔
1488

1489
TEST_F(TSerialClientIntegrationTest, SlaveIdCollision)
16✔
1490
{
1491
    TTemplateMap t;
8✔
1492

1493
    auto factory = [=](const Json::Value& port_data, PRPCConfig rpcConfig) -> PFeaturePort {
12✔
1494
        auto path = port_data["path"].asString();
12✔
1495
        return std::make_shared<TFeaturePort>(std::make_shared<TFakeSerialPort>(*this, path, false), false);
36✔
1496
    };
4✔
1497

1498
    EXPECT_THROW(LoadConfig(GetDataFilePath("configs/config-collision-test.json"),
24✔
1499
                            DeviceFactory,
1500
                            CommonDeviceSchema,
1501
                            t,
1502
                            rpcConfig,
1503
                            PortsSchema,
1504
                            *ProtocolSchemas,
1505
                            factory),
1506
                 TConfigParserException);
1507

1508
    EXPECT_THROW(LoadConfig(GetDataFilePath("configs/config-collision-test2.json"),
24✔
1509
                            DeviceFactory,
1510
                            CommonDeviceSchema,
1511
                            t,
1512
                            rpcConfig,
1513
                            PortsSchema,
1514
                            *ProtocolSchemas,
1515
                            factory),
1516
                 TConfigParserException);
1517

1518
    EXPECT_THROW(LoadConfig(GetDataFilePath("configs/config-collision-test3.json"),
24✔
1519
                            DeviceFactory,
1520
                            CommonDeviceSchema,
1521
                            t,
1522
                            rpcConfig,
1523
                            PortsSchema,
1524
                            *ProtocolSchemas,
1525
                            factory),
1526
                 TConfigParserException);
1527

1528
    EXPECT_NO_THROW(LoadConfig(GetDataFilePath("configs/config-no-collision-test.json"),
8✔
1529
                               DeviceFactory,
1530
                               CommonDeviceSchema,
1531
                               t,
1532
                               rpcConfig,
1533
                               PortsSchema,
1534
                               *ProtocolSchemas,
1535
                               factory));
1536
}
4✔
1537

1538
/* This function checks Serial Driver behaviour when RPC request and value publishing event occurs
1539
 * Writing new values, then RPC IO through serial port and reading values are expected */
1540

1541
TRPCResultCode TSerialClientIntegrationTest::SendRPCRequest(PMQTTSerialDriver serialDriver,
6✔
1542
                                                            std::vector<int> expectedRequest,
1543
                                                            std::vector<int> expectedResponse,
1544
                                                            size_t expectedResponseLength,
1545
                                                            std::chrono::seconds totalTimeout)
1546
{
1547
    PublishWaitOnValue("/devices/RPCTest/controls/RGB/on", "10;20;30", 0, true);
6✔
1548
    PublishWaitOnValue("/devices/RPCTest/controls/White/on", "42", 0, true);
6✔
1549

1550
    std::vector<PSerialPortDriver> portDrivers = serialDriver->GetPortDrivers();
12✔
1551
    PSerialClient serialClient = portDrivers[0]->GetSerialClient();
12✔
1552

1553
    TRPCResultCode resultCode = TRPCResultCode::RPC_OK;
6✔
1554
    std::vector<int> responseInt;
12✔
1555
    Json::Value request;
12✔
1556
    request["response_timeout"] = 500;
6✔
1557
    request["frame_timeout"] = 20;
6✔
1558
    request["total_timeout"] = chrono::duration_cast<chrono::milliseconds>(totalTimeout).count();
6✔
1559
    request["response_size"] = expectedResponseLength;
6✔
1560
    std::vector<uint8_t> requestUint;
6✔
1561
    std::copy(expectedRequest.begin(), expectedRequest.end(), back_inserter(requestUint));
6✔
1562
    request["msg"] = FormatResponse(requestUint, TRPCMessageFormat::RPC_MESSAGE_FORMAT_HEX);
6✔
1563
    request["format"] = "HEX";
6✔
1564
    auto onResult = [&responseInt](const Json::Value& response) {
8✔
1565
        auto str = HexStringToByteVector(response["response"].asString());
8✔
1566
        std::copy(str.begin(), str.end(), back_inserter(responseInt));
4✔
1567
    };
4✔
1568
    auto onError = [&resultCode](const TMqttRpcErrorCode code, const std::string&) {
4✔
1569
        resultCode =
2✔
1570
            code == WBMQTT::E_RPC_REQUEST_TIMEOUT ? TRPCResultCode::RPC_WRONG_TIMEOUT : TRPCResultCode::RPC_WRONG_IO;
2✔
1571
    };
8✔
1572

1573
    try {
1574
        Note() << "Send RPC request";
6✔
1575
        auto task = std::make_shared<TRPCPortLoadRawSerialClientTask>(request, onResult, onError);
12✔
1576
        serialClient->AddTask(task);
6✔
1577
        SerialDriver->LoopOnce();
6✔
1578
        EXPECT_EQ(responseInt == expectedResponse, true);
6✔
1579
    } catch (const TRPCException& exception) {
×
1580
        resultCode = exception.GetResultCode();
×
1581
    }
1582

1583
    return resultCode;
12✔
1584
}
1585

1586
/* RPC Request sending test cases:
1587
 * 1. ReadFrame timeout (port IO exception)
1588
 * 2. RPC request timeout
1589
 * 3. Successful RPC request execution
1590
 * 4. Successful RPC request execution with zero length read
1591
 */
1592

1593
TEST_F(TSerialClientIntegrationTest, RPCRequestTransceive)
16✔
1594
{
1595
    TTemplateMap t;
8✔
1596

1597
    Config = LoadConfig(
4✔
1598
        GetDataFilePath("configs/config-rpc-test.json"),
8✔
1599
        DeviceFactory,
4✔
1600
        CommonDeviceSchema,
4✔
1601
        t,
1602
        rpcConfig,
4✔
1603
        PortsSchema,
4✔
1604
        *ProtocolSchemas,
4✔
1605
        [=](const Json::Value&, PRPCConfig rpcConfig) { return std::make_shared<TFeaturePort>(Port, false); });
16✔
1606
    FixFakeDevices(Config);
4✔
1607

1608
    FilterConfig("RPCTest");
4✔
1609

1610
    SerialDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
4✔
1611

1612
    auto device = TFakeSerialDevice::GetDevice("0x98");
4✔
1613

1614
    if (!device) {
4✔
1615
        throw std::runtime_error("device not found or wrong type");
×
1616
    }
1617

1618
    device->SetSessionLogEnabled(true);
4✔
1619
    std::vector<int> expectedRequest = {0x16, 0x05, 0x00, 0x0a, 0xff, 0x00, 0xaf, 0x1f};
8✔
1620
    std::vector<int> expectedResponse = {0x16, 0x05, 0x00, 0x0a, 0xff, 0x00, 0xaf, 0x1f};
8✔
1621
    std::vector<int> emptyVector;
8✔
1622

1623
    Note() << "LoopOnce() [first start]";
4✔
1624
    SerialDriver->LoopOnce();
4✔
1625
    SerialDriver->LoopOnce();
4✔
1626

1627
    // ReadFrame timeout case
1628
    Note() << "[test case] ReadFrame exception: nothing to read";
4✔
1629
    Port->Expect(expectedRequest, emptyVector, NULL);
4✔
1630
    EXPECT_EQ(
4✔
1631
        SendRPCRequest(SerialDriver, expectedRequest, emptyVector, expectedResponse.size(), std::chrono::seconds(12)),
1632
        TRPCResultCode::RPC_WRONG_IO);
1633

1634
    // Successful case
1635
    Note() << "[test case] RPC successful case";
4✔
1636
    Port->Expect(expectedRequest, expectedResponse, NULL);
4✔
1637
    EXPECT_EQ(SendRPCRequest(SerialDriver,
4✔
1638
                             expectedRequest,
1639
                             expectedResponse,
1640
                             expectedResponse.size(),
1641
                             std::chrono::seconds(12)),
1642
              TRPCResultCode::RPC_OK);
1643

1644
    // Read zero length response
1645
    Note() << "[test case] RPC request with zero length read";
4✔
1646
    Port->Expect(expectedRequest, emptyVector, NULL);
4✔
1647
    EXPECT_EQ(SendRPCRequest(SerialDriver, expectedRequest, emptyVector, 0, std::chrono::seconds(12)),
4✔
1648
              TRPCResultCode::RPC_OK);
1649
}
4✔
1650

1651
/** Reconnect test cases **/
1652
/*
1653
    Test configurations:
1654
    1) 1 device; 1 poll interval for all registers;
1655
    2) 1 device; one long poll interval for one registers, one short poll interval for the rest of registers;
1656
    3) 2 devices; 1 poll interval for all registers; only 1 device disconnect - connect
1657

1658
    Test conditions:
1659
    1) device_timeout_ms only
1660
    2) device_fail_cycles only
1661
    3) device_timeout_ms and device_fail_cycles
1662
*/
1663

1664
PMQTTSerialDriver TSerialClientIntegrationTest::StartReconnectTest1Device(bool miss, bool pollIntervalTest)
10✔
1665
{
1666
    TTemplateMap t;
20✔
1667
    Config =
1668
        LoadConfig(GetDataFilePath("configs/reconnect_test_1_device.json"),
30✔
1669
                   DeviceFactory,
10✔
1670
                   CommonDeviceSchema,
10✔
1671
                   t,
1672
                   rpcConfig,
10✔
1673
                   PortsSchema,
10✔
1674
                   *ProtocolSchemas,
10✔
1675
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
50✔
1676
    FixFakeDevices(Config);
10✔
1677

1678
    if (pollIntervalTest) {
10✔
1679
        Config->PortConfigs[0]->Devices[0]->Channels[0]->Registers[0]->GetConfig()->ReadPeriod = 100s;
2✔
1680
    }
1681

1682
    PMQTTSerialDriver mqttDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
10✔
1683

1684
    auto device = TFakeSerialDevice::GetDevice("12");
10✔
1685

1686
    { // Test initial WriteInitValues
1687
        Note() << "LoopOnce() [first start]";
10✔
1688
        mqttDriver->LoopOnce();
10✔
1689

1690
        EXPECT_EQ(42, device->Registers[1]);
10✔
1691
        EXPECT_EQ(24, device->Registers[2]);
10✔
1692
    }
1693

1694
    { // Test read
1695
        Note() << "LoopOnce()";
10✔
1696
        mqttDriver->LoopOnce();
10✔
1697
    }
1698

1699
    { // Device disconnected
1700
        Note() << "SimulateDisconnect(true)";
10✔
1701
        device->SetIsConnected(false);
10✔
1702

1703
        {
1704
            bool by_timeout = Device->DeviceConfig()->DeviceTimeout.count() > 0;
10✔
1705
            bool by_cycles = Device->DeviceConfig()->DeviceMaxFailCycles > 0;
10✔
1706

1707
            if (by_timeout) {
10✔
1708
                auto delay = Device->DeviceConfig()->DeviceTimeout;
10✔
1709

1710
                if (miss) {
10✔
1711
                    delay -= chrono::milliseconds(100);
2✔
1712
                }
1713

1714
                if (by_cycles) {
10✔
1715
                    auto disconnectTimepoint = std::chrono::steady_clock::now();
10✔
1716

1717
                    // Couple of unsuccessful reads
1718
                    while (std::chrono::steady_clock::now() - disconnectTimepoint < delay) {
30✔
1719
                        Note() << "LoopOnce()";
20✔
1720
                        mqttDriver->LoopOnce();
20✔
1721
                        usleep(std::chrono::duration_cast<std::chrono::microseconds>(delay).count() /
20✔
1722
                               device->DeviceConfig()->DeviceMaxFailCycles);
20✔
1723
                    }
1724
                } else {
1725
                    auto disconnectTimepoint = std::chrono::steady_clock::now();
×
1726

1727
                    // Couple of unsuccessful reads
1728
                    while (std::chrono::steady_clock::now() - disconnectTimepoint < delay) {
×
1729
                        Note() << "LoopOnce()";
×
1730
                        mqttDriver->LoopOnce();
×
1731
                        usleep(std::chrono::duration_cast<std::chrono::microseconds>(delay).count() / 10);
×
1732
                    }
1733
                }
1734
            } else if (by_cycles) {
×
1735
                // Couple of unsuccessful reads
1736
                auto remainingCycles = Device->DeviceConfig()->DeviceMaxFailCycles;
×
1737

1738
                if (miss) {
×
1739
                    --remainingCycles;
×
1740
                }
1741

1742
                while (remainingCycles--) {
×
1743
                    Note() << "LoopOnce()";
×
1744
                    mqttDriver->LoopOnce();
×
1745
                }
1746
            } else {
1747
                auto disconnectTimepoint = std::chrono::steady_clock::now();
×
1748
                auto delay = chrono::milliseconds(10000);
×
1749

1750
                // Couple of unsuccessful reads
1751
                while (std::chrono::steady_clock::now() - disconnectTimepoint < delay) {
×
1752
                    Note() << "LoopOnce()";
×
1753
                    mqttDriver->LoopOnce();
×
1754
                    usleep(std::chrono::duration_cast<std::chrono::microseconds>(delay).count() / 10);
×
1755
                }
1756
            }
1757
        }
1758

1759
        // Final unsuccessful read after timeout, after this loop we expect device to be counted as disconnected
1760
        Note() << "LoopOnce()";
10✔
1761
        mqttDriver->LoopOnce();
10✔
1762
    }
1763

1764
    return mqttDriver;
20✔
1765
}
1766

1767
PMQTTSerialDriver TSerialClientIntegrationTest::StartReconnectTest2Devices()
2✔
1768
{
1769
    TTemplateMap t;
4✔
1770
    Config =
1771
        LoadConfig(GetDataFilePath("configs/reconnect_test_2_devices.json"),
6✔
1772
                   DeviceFactory,
2✔
1773
                   CommonDeviceSchema,
2✔
1774
                   t,
1775
                   rpcConfig,
2✔
1776
                   PortsSchema,
2✔
1777
                   *ProtocolSchemas,
2✔
1778
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
10✔
1779
    FixFakeDevices(Config);
2✔
1780

1781
    PMQTTSerialDriver mqttDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
2✔
1782

1783
    auto dev1 = TFakeSerialDevice::GetDevice("12");
2✔
1784
    auto dev2 = TFakeSerialDevice::GetDevice("13");
2✔
1785

1786
    { // Test initial setup
1787
        Note() << "LoopOnce() [first start]";
2✔
1788
        mqttDriver->LoopOnce();
2✔
1789
        mqttDriver->LoopOnce();
2✔
1790

1791
        EXPECT_EQ(42, dev1->Registers[1]);
2✔
1792
        EXPECT_EQ(24, dev1->Registers[2]);
2✔
1793

1794
        EXPECT_EQ(32, dev2->Registers[1]);
2✔
1795
        EXPECT_EQ(64, dev2->Registers[2]);
2✔
1796
    }
1797

1798
    { // Test read
1799
        Note() << "LoopOnce()";
2✔
1800
        mqttDriver->LoopOnce();
2✔
1801
        mqttDriver->LoopOnce();
2✔
1802
    }
1803

1804
    { // Device disconnected
1805
        Note() << "SimulateDisconnect(true)";
2✔
1806
        dev1->SetIsConnected(false);
2✔
1807

1808
        bool by_timeout = Device->DeviceConfig()->DeviceTimeout.count() > 0;
2✔
1809
        bool by_cycles = Device->DeviceConfig()->DeviceMaxFailCycles > 0;
2✔
1810

1811
        if (by_timeout) {
2✔
1812
            if (by_cycles) {
2✔
1813
                auto disconnectTimepoint = std::chrono::steady_clock::now();
2✔
1814

1815
                // Couple of unsuccessful reads
1816
                while (std::chrono::steady_clock::now() - disconnectTimepoint < Device->DeviceConfig()->DeviceTimeout) {
6✔
1817
                    Note() << "LoopOnce()";
4✔
1818
                    mqttDriver->LoopOnce();
4✔
1819
                    mqttDriver->LoopOnce();
4✔
1820
                    usleep(std::chrono::duration_cast<std::chrono::microseconds>(Device->DeviceConfig()->DeviceTimeout)
8✔
1821
                               .count() /
4✔
1822
                           Device->DeviceConfig()->DeviceMaxFailCycles);
4✔
1823
                }
1824
            } else {
1825
                auto disconnectTimepoint = std::chrono::steady_clock::now();
×
1826

1827
                // Couple of unsuccessful reads
1828
                while (std::chrono::steady_clock::now() - disconnectTimepoint < Device->DeviceConfig()->DeviceTimeout) {
×
1829
                    Note() << "LoopOnce()";
×
1830
                    mqttDriver->LoopOnce();
×
1831
                    mqttDriver->LoopOnce();
×
1832
                    usleep(std::chrono::duration_cast<std::chrono::microseconds>(Device->DeviceConfig()->DeviceTimeout)
×
1833
                               .count() /
×
1834
                           10);
1835
                }
1836
            }
1837
        } else if (by_cycles) {
×
1838
            // Couple of unsuccessful reads
1839
            auto remainingCycles = Device->DeviceConfig()->DeviceMaxFailCycles;
×
1840

1841
            while (remainingCycles--) {
×
1842
                Note() << "LoopOnce()";
×
1843
                mqttDriver->LoopOnce();
×
1844
                mqttDriver->LoopOnce();
×
1845
            }
1846
        } else {
1847
            auto disconnectTimepoint = std::chrono::steady_clock::now();
×
1848
            auto delay = chrono::milliseconds(10000);
×
1849

1850
            // Couple of unsuccessful reads
1851
            while (std::chrono::steady_clock::now() - disconnectTimepoint < delay) {
×
1852
                Note() << "LoopOnce()";
×
1853
                mqttDriver->LoopOnce();
×
1854
                mqttDriver->LoopOnce();
×
1855
                usleep(std::chrono::duration_cast<std::chrono::microseconds>(delay).count() / 10);
×
1856
            }
1857
        }
1858

1859
        // Final unsuccessful read after timeout, after this loop we expect device to be counted as disconnected
1860
        Note() << "LoopOnce()";
2✔
1861
        mqttDriver->LoopOnce();
2✔
1862
        mqttDriver->LoopOnce();
2✔
1863
    }
1864

1865
    return mqttDriver;
4✔
1866
}
1867

1868
void TSerialClientIntegrationTest::ReconnectTest1Device(function<void()>&& thunk, bool pollIntervalTest)
8✔
1869
{
1870
    auto observer = StartReconnectTest1Device(false, pollIntervalTest);
16✔
1871
    auto device = TFakeSerialDevice::GetDevice("12");
8✔
1872

1873
    device->Registers[1] = 0; // reset those to make sure that setup section is wrote again
8✔
1874
    device->Registers[2] = 0;
8✔
1875

1876
    thunk();
8✔
1877

1878
    { // Loop to check limited polling
1879
        Note() << "LoopOnce() (limited polling expected)";
8✔
1880
        observer->LoopOnce();
8✔
1881
    }
1882

1883
    { // Device is connected back
1884
        Note() << "SimulateDisconnect(false)";
8✔
1885
        device->SetIsConnected(true);
8✔
1886

1887
        // Disconnected device poll delayed for 500ms on every poll retry
1888
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
8✔
1889

1890
        Note() << "LoopOnce()";
8✔
1891
        observer->LoopOnce();
8✔
1892

1893
        EXPECT_EQ(42, device->Registers[1]);
8✔
1894
        EXPECT_EQ(24, device->Registers[2]);
8✔
1895
    }
1896
}
8✔
1897

1898
void TSerialClientIntegrationTest::ReconnectTest2Devices(function<void()>&& thunk)
2✔
1899
{
1900
    auto observer = StartReconnectTest2Devices();
4✔
1901

1902
    auto dev1 = TFakeSerialDevice::GetDevice("12");
2✔
1903
    auto dev2 = TFakeSerialDevice::GetDevice("13");
2✔
1904

1905
    dev1->Registers[1] = 0; // reset those to make sure that setup section is wrote again
2✔
1906
    dev1->Registers[2] = 0;
2✔
1907

1908
    dev2->Registers[1] = 1; // set control values to make sure that no setup section is written
2✔
1909
    dev2->Registers[2] = 2;
2✔
1910

1911
    thunk();
2✔
1912

1913
    { // Loop to check limited polling
1914
        Note() << "LoopOnce() (limited polling expected)";
2✔
1915
        observer->LoopOnce();
2✔
1916
        observer->LoopOnce();
2✔
1917
    }
1918

1919
    { // Device is connected back
1920
        Note() << "SimulateDisconnect(false)";
2✔
1921
        dev1->SetIsConnected(true);
2✔
1922

1923
        // Disconnected device poll delayed for 500ms on every poll retry
1924
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
2✔
1925

1926
        Note() << "LoopOnce()";
2✔
1927
        auto future = MqttBroker->WaitForPublish("/devices/reconnect-test-1/controls/I2/meta/error");
6✔
1928
        observer->LoopOnce();
2✔
1929
        observer->LoopOnce();
2✔
1930

1931
        EXPECT_EQ(42, dev1->Registers[1]);
2✔
1932
        EXPECT_EQ(24, dev1->Registers[2]);
2✔
1933

1934
        EXPECT_EQ(1, dev2->Registers[1]);
2✔
1935
        EXPECT_EQ(2, dev2->Registers[2]);
2✔
1936
    }
1937
}
2✔
1938

1939
TEST_F(TSerialClientIntegrationTest, ReconnectTimeout)
16✔
1940
{
1941
    ReconnectTest1Device([&] { DeviceTimeoutOnly(Device, DefaultDeviceTimeout); });
6✔
1942
}
4✔
1943

1944
TEST_F(TSerialClientIntegrationTest, ReconnectCycles)
16✔
1945
{
1946
    ReconnectTest1Device([&] { DeviceMaxFailCyclesOnly(Device, 10); });
6✔
1947
}
4✔
1948

1949
TEST_F(TSerialClientIntegrationTest, ReconnectTimeoutAndCycles)
16✔
1950
{
1951
    ReconnectTest1Device([&] { DeviceTimeoutAndMaxFailCycles(Device, DefaultDeviceTimeout, 10); });
6✔
1952
}
4✔
1953

1954
TEST_F(TSerialClientIntegrationTest, ReconnectRegisterWithBigPollInterval)
16✔
1955
{
1956
    auto t1 = chrono::steady_clock::now();
4✔
1957

1958
    ReconnectTest1Device([&] { DeviceTimeoutOnly(Device, DefaultDeviceTimeout); }, true);
6✔
1959

1960
    auto time = chrono::steady_clock::now() - t1;
4✔
1961

1962
    EXPECT_LT(time, chrono::seconds(100));
4✔
1963
}
4✔
1964

1965
TEST_F(TSerialClientIntegrationTest, Reconnect2)
16✔
1966
{
1967
    ReconnectTest2Devices([&] { DeviceTimeoutOnly(Device, DefaultDeviceTimeout); });
6✔
1968
}
4✔
1969

1970
TEST_F(TSerialClientIntegrationTest, ReconnectMiss)
16✔
1971
{
1972
    auto observer = StartReconnectTest1Device(true);
8✔
1973

1974
    auto device = TFakeSerialDevice::GetDevice("12");
4✔
1975
    if (!device) {
4✔
1976
        throw std::runtime_error("device not found");
×
1977
    }
1978

1979
    device->Registers[1] = 1; // set control values to make sure that no setup section is written
4✔
1980
    device->Registers[2] = 2;
4✔
1981

1982
    { // Device is connected back before timeout
1983
        Note() << "SimulateDisconnect(false)";
4✔
1984
        device->SetIsConnected(true);
4✔
1985

1986
        Note() << "LoopOnce()";
4✔
1987
        auto future = MqttBroker->WaitForPublish("/devices/reconnect-test/controls/I2");
12✔
1988
        observer->LoopOnce();
4✔
1989
        future.Wait();
4✔
1990

1991
        EXPECT_EQ(1, device->Registers[1]);
4✔
1992
        EXPECT_EQ(2, device->Registers[2]);
4✔
1993
    }
1994
}
4✔
1995

1996
TEST_F(TSerialClientIntegrationTest, ReconnectOnPortWriteError)
16✔
1997
{
1998
    // The test simulates reconnection on EBADF error after writing first setup register
1999
    // The behavior is a result of bad hwconf setup
2000
    TTemplateMap t;
8✔
2001
    Config =
2002
        LoadConfig(GetDataFilePath("configs/reconnect_test_ebadf.json"),
12✔
2003
                   DeviceFactory,
4✔
2004
                   CommonDeviceSchema,
4✔
2005
                   t,
2006
                   rpcConfig,
4✔
2007
                   PortsSchema,
4✔
2008
                   *ProtocolSchemas,
4✔
2009
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
16✔
2010
    FixFakeDevices(Config);
4✔
2011

2012
    PMQTTSerialDriver mqttDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
8✔
2013

2014
    Port->SimulateDisconnect(TFakeSerialPort::BadFileDescriptorOnWriteAndRead);
4✔
2015

2016
    for (auto i = 0; i < 3; ++i) {
16✔
2017
        Note() << "LoopOnce()";
12✔
2018
        mqttDriver->LoopOnce();
12✔
2019
    }
2020

2021
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
4✔
2022

2023
    for (auto i = 0; i < 3; ++i) {
16✔
2024
        Note() << "LoopOnce()";
12✔
2025
        mqttDriver->LoopOnce();
12✔
2026
    }
2027
}
4✔
2028

2029
TEST_F(TSerialClientIntegrationTest, OnTopicWriteError)
16✔
2030
{
2031
    // The test simulates EBADF error during register write after receiving a message from /on topic
2032
    TTemplateMap t;
8✔
2033
    Config =
2034
        LoadConfig(GetDataFilePath("configs/reconnect_test_ebadf.json"),
12✔
2035
                   DeviceFactory,
4✔
2036
                   CommonDeviceSchema,
4✔
2037
                   t,
2038
                   rpcConfig,
4✔
2039
                   PortsSchema,
4✔
2040
                   *ProtocolSchemas,
4✔
2041
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
16✔
2042
    FixFakeDevices(Config);
4✔
2043

2044
    PMQTTSerialDriver mqttDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
8✔
2045

2046
    Port->SimulateDisconnect(TFakeSerialPort::BadFileDescriptorOnWriteAndRead);
4✔
2047

2048
    Note() << "LoopOnce()";
4✔
2049
    mqttDriver->LoopOnce();
4✔
2050

2051
    PublishWaitOnValue("/devices/test/controls/I1/on", "42", 1, true);
4✔
2052

2053
    Note() << "LoopOnce()";
4✔
2054
    mqttDriver->LoopOnce();
4✔
2055
}
4✔
2056

2057
TEST_F(TSerialClientIntegrationTest, ReconnectAfterNetworkDisconnect)
16✔
2058
{
2059
    // The test simulates reconnection after loosing network connection to TCP-based device
2060
    // A socket is alive, writes are successful, reads fail with timeout
2061
    // The logic must close an reopen port
2062
    TTemplateMap t;
8✔
2063
    Config =
2064
        LoadConfig(GetDataFilePath("configs/reconnect_test_network.json"),
12✔
2065
                   DeviceFactory,
4✔
2066
                   CommonDeviceSchema,
4✔
2067
                   t,
2068
                   rpcConfig,
4✔
2069
                   PortsSchema,
4✔
2070
                   *ProtocolSchemas,
4✔
2071
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
16✔
2072
    FixFakeDevices(Config);
4✔
2073

2074
    PMQTTSerialDriver mqttDriver = make_shared<TMQTTSerialDriver>(Driver, Config);
8✔
2075

2076
    auto device = TFakeSerialDevice::GetDevice("12");
4✔
2077
    if (!device) {
4✔
2078
        throw std::runtime_error("device not found");
×
2079
    }
2080

2081
    // successful reads
2082
    for (auto i = 0; i < 3; ++i) {
16✔
2083
        Note() << "LoopOnce()";
12✔
2084
        mqttDriver->LoopOnce();
12✔
2085
    }
2086

2087
    // read errors
2088
    device->BlockReadFor(1, true);
4✔
2089

2090
    std::this_thread::sleep_for(std::chrono::milliseconds(70));
4✔
2091

2092
    for (auto i = 0; i < 3; ++i) {
16✔
2093
        Note() << "LoopOnce()";
12✔
2094
        mqttDriver->LoopOnce();
12✔
2095
    }
2096

2097
    // device is disconnected
2098
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
4✔
2099
    mqttDriver->LoopOnce();
4✔
2100

2101
    // port reopen
2102
    device->BlockReadFor(1, false);
4✔
2103

2104
    for (auto i = 0; i < 3; ++i) {
16✔
2105
        Note() << "LoopOnce()";
12✔
2106
        mqttDriver->LoopOnce();
12✔
2107
    }
2108
}
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