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

wirenboard / wb-mqtt-serial / 6

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

Pull #1049

github

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

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

830.18 hits per line

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

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)
266✔
33
    {
34
        return ConvertFromRawValue(*reg->GetConfig(), reg->GetValue());
532✔
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)
206✔
44
    {
45
        if (LastRegErrors.count(reg) && LastRegErrors[reg] == reg->GetErrorState()) {
206✔
46
            return;
144✔
47
        }
48
        std::string what;
124✔
49
        const std::vector<std::string> errorNames = {"read", "write", "poll interval miss"};
372✔
50
        for (size_t i = 0; i < TRegister::TError::MAX_ERRORS; ++i) {
248✔
51
            if (reg->GetErrorState().test(i)) {
186✔
52
                if (!what.empty()) {
13✔
53
                    what += "+";
2✔
54
                }
55
                if (i < errorNames.size()) {
13✔
56
                    what += errorNames[i];
13✔
57
                } else {
58
                    what += "unknown";
×
59
                }
60
            }
61
        }
62
        if (what.empty()) {
62✔
63
            what = "no";
51✔
64
        }
65
        Emit() << "Error Callback: <" << reg->Device()->ToString() << ":" << reg->GetConfig()->TypeName << ": "
124✔
66
               << reg->GetConfig()->GetAddress() << ">: " << what << " error";
62✔
67
        LastRegErrors[reg] = reg->GetErrorState();
62✔
68
    }
69

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

76
    void SetUp();
77
    void TearDown();
78
    PRegister Reg(int addr,
50✔
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,
100✔
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));
150✔
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()
1✔
116
    {
1✔
117
        PortOpenCloseSettings.ReopenTimeout = std::chrono::milliseconds(700);
1✔
118
    }
1✔
119
};
120

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

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

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

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

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

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

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

178
TEST_F(TSerialClientTest, PortOpenError)
8✔
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);
4✔
184
    PRegister reg1 = Reg(1, U8);
4✔
185

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

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

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

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

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

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

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

205
TEST_F(TSerialClientReopenTest, ReopenTimeout)
8✔
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);
4✔
211
    PRegister reg1 = Reg(1, U8);
4✔
212

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

994
TEST_F(TSerialClientTestWithSetupRegisters, SetupFail)
8✔
995
{
996
    PRegister reg20 = Reg(20);
4✔
997
    SerialClient->AddDevice(Device);
2✔
998
    Device->BlockWriteFor(101, true);
2✔
999
    SerialClient->Cycle();
2✔
1000
    EXPECT_EQ(Device->GetConnectionState(), TDeviceConnectionState::DISCONNECTED);
2✔
1001
    Device->BlockWriteFor(101, false);
2✔
1002
    SerialClient->Cycle();
2✔
1003
    EXPECT_EQ(Device->GetConnectionState(), TDeviceConnectionState::CONNECTED);
2✔
1004
}
2✔
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()
19✔
1054
{
1055
    SetMode(E_Unordered);
19✔
1056
    TSerialClientTest::SetUp();
19✔
1057
    std::filesystem::remove(DB_PATH);
19✔
1058

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

1070
    Driver->StartLoop();
19✔
1071

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1333
    FilterConfig("DDL24");
2✔
1334

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1498
    EXPECT_THROW(LoadConfig(GetDataFilePath("configs/config-collision-test.json"),
12✔
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"),
12✔
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"),
12✔
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"),
4✔
1529
                               DeviceFactory,
1530
                               CommonDeviceSchema,
1531
                               t,
1532
                               rpcConfig,
1533
                               PortsSchema,
1534
                               *ProtocolSchemas,
1535
                               factory));
1536
}
2✔
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,
3✔
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);
3✔
1548
    PublishWaitOnValue("/devices/RPCTest/controls/White/on", "42", 0, true);
3✔
1549

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

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

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

1583
    return resultCode;
6✔
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)
8✔
1594
{
1595
    TTemplateMap t;
4✔
1596

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

1608
    FilterConfig("RPCTest");
2✔
1609

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

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

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

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

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

1627
    // ReadFrame timeout case
1628
    Note() << "[test case] ReadFrame exception: nothing to read";
2✔
1629
    Port->Expect(expectedRequest, emptyVector, NULL);
2✔
1630
    EXPECT_EQ(
2✔
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";
2✔
1636
    Port->Expect(expectedRequest, expectedResponse, NULL);
2✔
1637
    EXPECT_EQ(SendRPCRequest(SerialDriver,
2✔
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";
2✔
1646
    Port->Expect(expectedRequest, emptyVector, NULL);
2✔
1647
    EXPECT_EQ(SendRPCRequest(SerialDriver, expectedRequest, emptyVector, 0, std::chrono::seconds(12)),
2✔
1648
              TRPCResultCode::RPC_OK);
1649
}
2✔
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)
5✔
1665
{
1666
    TTemplateMap t;
10✔
1667
    Config =
1668
        LoadConfig(GetDataFilePath("configs/reconnect_test_1_device.json"),
15✔
1669
                   DeviceFactory,
5✔
1670
                   CommonDeviceSchema,
5✔
1671
                   t,
1672
                   rpcConfig,
5✔
1673
                   PortsSchema,
5✔
1674
                   *ProtocolSchemas,
5✔
1675
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
25✔
1676
    FixFakeDevices(Config);
5✔
1677

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

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

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

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

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

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

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

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

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

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

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

1717
                    // Couple of unsuccessful reads
1718
                    while (std::chrono::steady_clock::now() - disconnectTimepoint < delay) {
15✔
1719
                        Note() << "LoopOnce()";
10✔
1720
                        mqttDriver->LoopOnce();
10✔
1721
                        usleep(std::chrono::duration_cast<std::chrono::microseconds>(delay).count() /
10✔
1722
                               device->DeviceConfig()->DeviceMaxFailCycles);
10✔
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()";
5✔
1761
        mqttDriver->LoopOnce();
5✔
1762
    }
1763

1764
    return mqttDriver;
10✔
1765
}
1766

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

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

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

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

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

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

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

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

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

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

1815
                // Couple of unsuccessful reads
1816
                while (std::chrono::steady_clock::now() - disconnectTimepoint < Device->DeviceConfig()->DeviceTimeout) {
3✔
1817
                    Note() << "LoopOnce()";
2✔
1818
                    mqttDriver->LoopOnce();
2✔
1819
                    mqttDriver->LoopOnce();
2✔
1820
                    usleep(std::chrono::duration_cast<std::chrono::microseconds>(Device->DeviceConfig()->DeviceTimeout)
4✔
1821
                               .count() /
2✔
1822
                           Device->DeviceConfig()->DeviceMaxFailCycles);
2✔
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()";
1✔
1861
        mqttDriver->LoopOnce();
1✔
1862
        mqttDriver->LoopOnce();
1✔
1863
    }
1864

1865
    return mqttDriver;
2✔
1866
}
1867

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

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

1876
    thunk();
4✔
1877

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

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

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

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

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

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

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

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

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

1911
    thunk();
1✔
1912

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

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

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

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

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

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

1939
TEST_F(TSerialClientIntegrationTest, ReconnectTimeout)
8✔
1940
{
1941
    ReconnectTest1Device([&] { DeviceTimeoutOnly(Device, DefaultDeviceTimeout); });
3✔
1942
}
2✔
1943

1944
TEST_F(TSerialClientIntegrationTest, ReconnectCycles)
8✔
1945
{
1946
    ReconnectTest1Device([&] { DeviceMaxFailCyclesOnly(Device, 10); });
3✔
1947
}
2✔
1948

1949
TEST_F(TSerialClientIntegrationTest, ReconnectTimeoutAndCycles)
8✔
1950
{
1951
    ReconnectTest1Device([&] { DeviceTimeoutAndMaxFailCycles(Device, DefaultDeviceTimeout, 10); });
3✔
1952
}
2✔
1953

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

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

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

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

1965
TEST_F(TSerialClientIntegrationTest, Reconnect2)
8✔
1966
{
1967
    ReconnectTest2Devices([&] { DeviceTimeoutOnly(Device, DefaultDeviceTimeout); });
3✔
1968
}
2✔
1969

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

1974
    auto device = TFakeSerialDevice::GetDevice("12");
2✔
1975
    if (!device) {
2✔
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
2✔
1980
    device->Registers[2] = 2;
2✔
1981

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

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

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

1996
TEST_F(TSerialClientIntegrationTest, ReconnectOnPortWriteError)
8✔
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;
4✔
2001
    Config =
2002
        LoadConfig(GetDataFilePath("configs/reconnect_test_ebadf.json"),
6✔
2003
                   DeviceFactory,
2✔
2004
                   CommonDeviceSchema,
2✔
2005
                   t,
2006
                   rpcConfig,
2✔
2007
                   PortsSchema,
2✔
2008
                   *ProtocolSchemas,
2✔
2009
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
8✔
2010
    FixFakeDevices(Config);
2✔
2011

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

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

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

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

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

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

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

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

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

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

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

2057
TEST_F(TSerialClientIntegrationTest, ReconnectAfterNetworkDisconnect)
8✔
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;
4✔
2063
    Config =
2064
        LoadConfig(GetDataFilePath("configs/reconnect_test_network.json"),
6✔
2065
                   DeviceFactory,
2✔
2066
                   CommonDeviceSchema,
2✔
2067
                   t,
2068
                   rpcConfig,
2✔
2069
                   PortsSchema,
2✔
2070
                   *ProtocolSchemas,
2✔
2071
                   [=](const Json::Value&, PRPCConfig config) { return std::make_shared<TFeaturePort>(Port, false); });
8✔
2072
    FixFakeDevices(Config);
2✔
2073

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

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

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

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

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

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

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

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

2104
    for (auto i = 0; i < 3; ++i) {
8✔
2105
        Note() << "LoopOnce()";
6✔
2106
        mqttDriver->LoopOnce();
6✔
2107
    }
2108
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc