• 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

99.01
/test/serial_config_test.cpp
1
#include <algorithm>
2
#include <wblib/testing/testlog.h>
3

4
#include "confed_schema_generator.h"
5
#include "config_merge_template.h"
6
#include "config_schema_generator.h"
7
#include "fake_serial_device.h"
8
#include "file_utils.h"
9
#include "serial_config.h"
10
#include "serial_device.h"
11
#include "test_utils.h"
12

13
using namespace std;
14
using namespace WBMQTT;
15
using namespace WBMQTT::Testing;
16

17
class TConfigParserTest: public TLoggedFixture
18
{
19
protected:
20
    TSerialDeviceFactory DeviceFactory;
21
    PRPCConfig RPCConfig = std::make_shared<TRPCConfig>();
22

23
    void SetUp()
11✔
24
    {
25
        RegisterProtocols(DeviceFactory);
11✔
26
        TFakeSerialDevice::Register(DeviceFactory);
11✔
27
    }
11✔
28

29
    void PrintConfig(PHandlerConfig config)
5✔
30
    {
31
        Emit() << "Debug: " << config->Debug;
5✔
32
        Emit() << "PublishParams: " << config->PublishParameters.Policy << " "
5✔
33
               << config->PublishParameters.PublishUnchangedInterval.count();
5✔
34
        Emit() << "Ports:";
5✔
35
        for (auto port_config: config->PortConfigs) {
17✔
36
            TTestLogIndent indent(*this);
12✔
37
            Emit() << "------";
12✔
38
            Emit() << "ConnSettings: " << port_config->Port->GetDescription();
12✔
39
            Emit() << "ConnectionMaxFailCycles: " << port_config->OpenCloseSettings.ConnectionMaxFailCycles;
12✔
40
            Emit() << "MaxFailTime: " << port_config->OpenCloseSettings.MaxFailTime.count();
12✔
41
            if (port_config->ReadRateLimit) {
12✔
42
                Emit() << "ReadRateLimit: " << port_config->ReadRateLimit->count();
4✔
43
            }
44
            Emit() << "GuardInterval: " << port_config->RequestDelay.count();
12✔
45
            Emit() << "Response timeout: "
12✔
46
                   << std::chrono::duration_cast<std::chrono::milliseconds>(
×
47
                          port_config->Port->GetMinimalResponseTimeout())
12✔
48
                          .count();
12✔
49

50
            if (port_config->Devices.empty()) {
12✔
51
                Emit() << "No device configs.";
2✔
52
                continue;
2✔
53
            }
54
            Emit() << "DeviceConfigs:";
10✔
55
            for (auto device: port_config->Devices) {
31✔
56
                auto device_config = device->Device->DeviceConfig();
21✔
57
                TTestLogIndent indent(*this);
21✔
58
                Emit() << "------";
21✔
59
                Emit() << "Id: " << device_config->Id;
21✔
60
                Emit() << "Name: " << device_config->Name;
21✔
61
                Emit() << "SlaveId: " << device_config->SlaveId;
21✔
62
                Emit() << "MaxRegHole: " << device_config->MaxRegHole;
21✔
63
                Emit() << "MaxBitHole: " << device_config->MaxBitHole;
21✔
64
                Emit() << "MaxReadRegisters: " << device_config->MaxReadRegisters;
21✔
65
                Emit() << "MinReadRegisters: " << device_config->MinReadRegisters;
21✔
66
                Emit() << "GuardInterval: " << device_config->RequestDelay.count();
21✔
67
                Emit() << "DeviceTimeout: " << device_config->DeviceTimeout.count();
21✔
68
                Emit() << "Response timeout: " << device_config->ResponseTimeout.count();
21✔
69
                Emit() << "Frame timeout: " << device_config->FrameTimeout.count();
21✔
70
                if (!device->Channels.empty()) {
21✔
71
                    Emit() << "DeviceChannels:";
21✔
72
                    for (auto device_channel: device->Channels) {
85✔
73
                        TTestLogIndent indent(*this);
128✔
74
                        Emit() << "------";
64✔
75
                        Emit() << "Name: " << device_channel->GetName();
64✔
76
                        for (const auto& it: device_channel->GetTitles()) {
77✔
77
                            if (it.first != "en") {
13✔
78
                                Emit() << "Name " << it.first << ": " << it.second;
8✔
79
                            }
80
                        }
81
                        Emit() << "Type: " << device_channel->Type;
64✔
82
                        Emit() << "MqttId: " << device_channel->MqttId;
64✔
83
                        Emit() << "DeviceId: " << device_channel->DeviceId;
64✔
84
                        Emit() << "Order: " << device_channel->Order;
64✔
85
                        Emit() << "OnValue: " << device_channel->OnValue;
64✔
86
                        if (!device_channel->OffValue.empty()) {
64✔
87
                            Emit() << "OffValue: " << device_channel->OffValue;
1✔
88
                        }
89
                        if (isnan(device_channel->Max)) {
64✔
90
                            Emit() << "Max: not set";
63✔
91
                        } else {
92
                            Emit() << "Max: " << device_channel->Max;
1✔
93
                        }
94
                        if (isnan(device_channel->Min)) {
64✔
95
                            Emit() << "Min: not set";
63✔
96
                        } else {
97
                            Emit() << "Min: " << device_channel->Min;
1✔
98
                        }
99
                        Emit() << "Precision: " << device_channel->Precision;
64✔
100
                        Emit() << "ReadOnly: " << device_channel->ReadOnly;
64✔
101
                        if (!device_channel->Registers.empty()) {
64✔
102
                            Emit() << "Registers:";
64✔
103
                        }
104
                        for (auto channelsRegister: device_channel->Registers) {
130✔
105
                            auto reg = channelsRegister->GetConfig();
132✔
106
                            TTestLogIndent indent(*this);
66✔
107
                            Emit() << "------";
66✔
108
                            Emit() << "Type and Address: " << reg;
66✔
109
                            Emit() << "Format: " << RegisterFormatName(reg->Format);
66✔
110
                            Emit() << "Scale: " << reg->Scale;
66✔
111
                            Emit() << "Offset: " << reg->Offset;
66✔
112
                            Emit() << "RoundTo: " << reg->RoundTo;
66✔
113
                            Emit() << "Poll: " << (reg->AccessType != TRegisterConfig::EAccessType::WRITE_ONLY);
66✔
114
                            Emit() << "ReadOnly: " << (reg->AccessType == TRegisterConfig::EAccessType::READ_ONLY);
66✔
115
                            Emit() << "TypeName: " << reg->TypeName;
66✔
116
                            if (reg->ReadPeriod) {
66✔
117
                                Emit() << "ReadPeriod: " << reg->ReadPeriod->count();
1✔
118
                            }
119
                            if (reg->ReadRateLimit) {
66✔
120
                                Emit() << "ReadRateLimit: " << reg->ReadRateLimit->count();
24✔
121
                            }
122
                            if (reg->ErrorValue) {
66✔
123
                                Emit() << "ErrorValue: " << *reg->ErrorValue;
7✔
124
                            } else {
125
                                Emit() << "ErrorValue: not set";
59✔
126
                            }
127
                            if (reg->UnsupportedValue) {
66✔
128
                                Emit() << "UnsupportedValue: " << *reg->UnsupportedValue;
1✔
129
                            } else {
130
                                Emit() << "UnsupportedValue: not set";
65✔
131
                            }
132
                            Emit() << "WordOrder: " << reg->WordOrder;
66✔
133
                        }
134
                    }
135

136
                    if (device->Device->GetSetupItems().empty())
21✔
137
                        continue;
16✔
138

139
                    Emit() << "SetupItems:";
5✔
140
                    for (auto setup_item: device->Device->GetSetupItems()) {
26✔
141
                        TTestLogIndent indent(*this);
21✔
142
                        Emit() << "------";
21✔
143
                        Emit() << "Name: " << setup_item->Name;
21✔
144
                        Emit() << "Address: " << setup_item->RegisterConfig->GetAddress();
21✔
145
                        Emit() << "Value: " << setup_item->HumanReadableValue;
21✔
146
                        if (setup_item->RawValue.GetType() == TRegisterValue::ValueType::String) {
21✔
147
                            Emit() << "RawValue: " << setup_item->RawValue;
×
148
                        } else {
149
                            Emit() << "RawValue: 0x" << std::setfill('0') << std::setw(2) << std::hex
42✔
150
                                   << setup_item->RawValue;
21✔
151
                        }
152
                        Emit() << "Reg type: " << setup_item->RegisterConfig->TypeName << " ("
42✔
153
                               << setup_item->RegisterConfig->Type << ")";
21✔
154
                        Emit() << "Reg format: " << RegisterFormatName(setup_item->RegisterConfig->Format);
21✔
155
                    }
156
                }
157
            }
158
        }
159
    }
5✔
160

161
    PHandlerConfig GetConfig(const std::string& filePath)
8✔
162
    {
163
        auto commonDeviceSchema(GetCommonDeviceSchema());
16✔
164
        auto portsSchema(WBMQTT::JSON::Parse(TLoggedFixture::GetDataFilePath("../wb-mqtt-serial-ports.schema.json")));
24✔
165
        TProtocolConfedSchemasMap protocolSchemas(TLoggedFixture::GetDataFilePath("../protocols"), commonDeviceSchema);
24✔
166
        TTemplateMap templateMap(GetTemplatesSchema());
8✔
167
        templateMap.AddTemplatesDir(GetDataFilePath("device-templates/"));
16✔
168
        return LoadConfig(GetDataFilePath(filePath),
16✔
169
                          DeviceFactory,
8✔
170
                          commonDeviceSchema,
171
                          templateMap,
172
                          RPCConfig,
8✔
173
                          portsSchema,
174
                          protocolSchemas);
16✔
175
    }
176
};
177

178
TEST_F(TConfigParserTest, Parse)
8✔
179
{
180
    PrintConfig(GetConfig("configs/parse_test.json"));
2✔
181
}
2✔
182

183
TEST_F(TConfigParserTest, ParseRateLimit)
8✔
184
{
185
    PrintConfig(GetConfig("configs/parse_test_rate_limit.json"));
2✔
186
}
2✔
187

188
TEST_F(TConfigParserTest, SameSetupItems)
8✔
189
{
190
    // Check that setup registers in config have higher priority than setup registers with same addresses from template
191
    PrintConfig(GetConfig("configs/parse_test_setup.json"));
2✔
192
}
2✔
193

194
TEST_F(TConfigParserTest, ParametersAsArray)
8✔
195
{
196
    // Check loading device template with parameters defined as array
197
    PrintConfig(GetConfig("configs/parse_test_parameters_array.json"));
2✔
198
}
2✔
199

200
TEST_F(TConfigParserTest, SetupCondition)
8✔
201
{
202
    // Check loading device template with setup with condition
203
    PrintConfig(GetConfig("configs/parse_test_setup_condition.json"));
2✔
204
}
2✔
205

206
TEST_F(TConfigParserTest, UnsuccessfulParse)
8✔
207
{
208
    auto commonDeviceSchema(GetCommonDeviceSchema());
4✔
209
    auto portsSchema(WBMQTT::JSON::Parse(TLoggedFixture::GetDataFilePath("../wb-mqtt-serial-ports.schema.json")));
6✔
210
    TProtocolConfedSchemasMap protocolSchemas(TLoggedFixture::GetDataFilePath("../protocols"), commonDeviceSchema);
6✔
211
    TTemplateMap templateMap(GetTemplatesSchema());
4✔
212
    templateMap.AddTemplatesDir(GetDataFilePath("parser_test/templates/"));
4✔
213
    IterateDirByPattern(
2✔
214
        GetDataFilePath("configs/unsuccessful"),
4✔
215
        "unsuccessful-",
216
        [&](const std::string& fname) {
11✔
217
            Emit() << "Parsing config " << fname;
11✔
218
            try {
219
                PHandlerConfig config = LoadConfig(fname,
220
                                                   DeviceFactory,
11✔
221
                                                   commonDeviceSchema,
11✔
222
                                                   templateMap,
11✔
223
                                                   RPCConfig,
11✔
224
                                                   portsSchema,
11✔
225
                                                   protocolSchemas);
33✔
226
            } catch (const std::exception& e) {
11✔
227
                Emit() << e.what();
11✔
228
            }
229
            return false;
11✔
230
        },
231
        true);
4✔
232
}
2✔
233

234
TEST_F(TConfigParserTest, MergeDeviceConfigWithTemplate)
8✔
235
{
236
    TTemplateMap templateMap(GetTemplatesSchema());
2✔
237
    templateMap.AddTemplatesDir(GetDataFilePath("parser_test/templates/"));
4✔
238

239
    for (auto i = 1; i <= 13; ++i) {
28✔
240
        auto deviceConfig(JSON::Parse(GetDataFilePath("parser_test/merge_template_ok" + to_string(i) + ".json")));
52✔
241
        std::string deviceType = deviceConfig.get("device_type", "").asString();
52✔
242
        auto mergedConfig(MergeDeviceConfigWithTemplate(deviceConfig,
243
                                                        deviceType,
244
                                                        templateMap.GetTemplate(deviceType)->GetTemplate()));
26✔
245
        auto res(JSON::Parse(GetDataFilePath("parser_test/merge_template_res" + to_string(i) + ".json")));
52✔
246
        ASSERT_TRUE(JsonsMatch(res, mergedConfig)) << i;
26✔
247
    }
248
}
249

250
TEST_F(TConfigParserTest, ProtocolParametersSchemaRef)
8✔
251
{
252
    for (const auto& name: DeviceFactory.GetProtocolNames()) {
50✔
253
        ASSERT_FALSE(DeviceFactory.GetCommonDeviceSchemaRef(name).empty()) << name;
48✔
254
    }
255
}
256

257
TEST_F(TConfigParserTest, ParseModbusDevideWithWriteAddress)
8✔
258
{
259
    auto portConfigs = GetConfig("configs/parse_test_modbus_write_address.json")->PortConfigs;
6✔
260
    EXPECT_FALSE(portConfigs.empty());
2✔
261
    auto devices = portConfigs[0]->Devices;
4✔
262
    EXPECT_FALSE(devices.empty());
2✔
263
    auto deviceChannels = devices[0]->Channels;
4✔
264
    EXPECT_FALSE(deviceChannels.empty());
2✔
265
    auto regs = deviceChannels[0]->Registers;
4✔
266
    EXPECT_FALSE(regs.empty());
2✔
267
    EXPECT_EQ(GetUint32RegisterAddress(regs[0]->GetConfig()->GetAddress()), 110);
2✔
268
    EXPECT_EQ(GetUint32RegisterAddress(regs[0]->GetConfig()->GetWriteAddress()), 115);
2✔
269
}
2✔
270

271
TEST_F(TConfigParserTest, ParseReadonlyParameters)
8✔
272
{
273
    auto portConfigs = GetConfig("configs/parse_test_readonly_parameters.json")->PortConfigs;
6✔
274
    EXPECT_FALSE(portConfigs.empty());
2✔
275
    auto devices = portConfigs[0]->Devices;
4✔
276
    EXPECT_FALSE(devices.empty());
2✔
277
    auto setupItems = devices[0]->Device->GetSetupItems();
4✔
278
    EXPECT_EQ(setupItems.size(), 1);
2✔
279
    EXPECT_EQ((*setupItems.begin())->Name, "p2");
2✔
280
}
2✔
281

282
TEST_F(TConfigParserTest, ParseEnum)
8✔
283
{
284
    auto portConfigs = GetConfig("configs/parse_enum.json")->PortConfigs;
6✔
285
    EXPECT_FALSE(portConfigs.empty());
2✔
286
    auto devices = portConfigs[0]->Devices;
4✔
287
    EXPECT_FALSE(devices.empty());
2✔
288
    auto deviceChannels = devices[0]->Channels;
4✔
289
    EXPECT_FALSE(deviceChannels.empty());
2✔
290
    auto titles1 = deviceChannels[0]->GetEnumTitles();
4✔
291
    EXPECT_EQ(titles1.size(), 2);
2✔
292
    EXPECT_EQ(titles1["0"]["en"], "zero");
6✔
293
    EXPECT_EQ(titles1["1"]["en"], "one");
6✔
294
    auto titles2 = deviceChannels[1]->GetEnumTitles();
4✔
295
    EXPECT_EQ(titles2.size(), 2);
2✔
296
    EXPECT_EQ(titles2["2"]["en"], "two");
6✔
297
    EXPECT_EQ(titles2["3"]["en"], "three");
6✔
298
}
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