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

wirenboard / wb-mqtt-db / 96

31 Jul 2026 10:43AM UTC coverage: 78.543% (-1.8%) from 80.337%
96

push

github

web-flow
Use exported vars instead of MAKEFLAGS for coverage options (#66)

938 of 1102 branches covered (85.12%)

1563 of 1990 relevant lines covered (78.54%)

13.11 hits per line

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

97.42
/test/dblogger.test.cpp
1
#include "config.h"
2
#include "sqlite_storage.h"
3
#include <gtest/gtest.h>
4
#include <stdio.h>
5
#include <wblib/testing/fake_mqtt.h>
6
#include <wblib/testing/testlog.h>
7
#include <wblib/wbmqtt.h>
8

9
using namespace std::chrono;
10

11
namespace
12
{
13
    class TFakeStorage: public IStorage
14
    {
15
        WBMQTT::Testing::TLoggedFixture& Fixture;
16
        bool HasRecords;
17
        uint64_t ChannelId;
18

19
    public:
20
        TFakeStorage(WBMQTT::Testing::TLoggedFixture& fixture): Fixture(fixture), HasRecords(false), ChannelId(1)
7✔
21
        {}
7✔
22

23
        PChannelInfo CreateChannel(const TChannelName& channelName) override
13✔
24
        {
25
            Fixture.Emit() << "Create channel " << channelName;
13✔
26
            return CreateChannelPrivate(ChannelId++, channelName.Device, channelName.Control);
13✔
27
        }
28

29
        void SetChannelPrecision(TChannelInfo& channelInfo, double precision) override
41✔
30
        {
31
            if (channelInfo.GetPrecision() != precision) {
41✔
32
                Fixture.Emit() << "Set precision for " << channelInfo.GetName() << " to " << precision;
14✔
33
                SetPrecision(channelInfo, precision);
14✔
34
            }
35
        }
41✔
36

37
        void WriteChannel(TChannelInfo& channelInfo,
41✔
38
                          const std::string& value,
39
                          const std::string& minimum,
40
                          const std::string& maximum,
41
                          bool retained,
42
                          std::chrono::system_clock::time_point time) override
43
        {
44
            Fixture.Emit() << "Storage data:";
41✔
45
            Fixture.Emit() << "  Value: " << value;
41✔
46
            Fixture.Emit() << "  Min: " << minimum;
41✔
47
            Fixture.Emit() << "  Max: " << maximum;
41✔
48
            HasRecords = true;
41✔
49
        }
41✔
50

51
        void Commit() override
67✔
52
        {
53
            if (HasRecords) {
67✔
54
                Fixture.Emit() << "Commit";
31✔
55
            }
56
            HasRecords = false;
67✔
57
        }
67✔
58

59
        void GetRecordsWithAveragingInterval(IRecordsVisitor& visitor,
×
60
                                             const std::vector<TChannelName>& channels,
61
                                             std::chrono::system_clock::time_point startTime,
62
                                             std::chrono::system_clock::time_point endTime,
63
                                             int64_t startId,
64
                                             uint32_t maxRecords,
65
                                             std::chrono::milliseconds minInterval) override
66
        {}
×
67

68
        void GetRecordsWithLimit(IRecordsVisitor& visitor,
×
69
                                 const std::vector<TChannelName>& channels,
70
                                 std::chrono::system_clock::time_point startTime,
71
                                 std::chrono::system_clock::time_point endTime,
72
                                 int64_t startId,
73
                                 uint32_t maxRecords,
74
                                 size_t overallRecordsLimit) override
75
        {}
×
76

77
        void GetChannels(IChannelVisitor& visitor) override
×
78
        {}
×
79
        void DeleteRecords(TChannelInfo& channel, uint32_t count) override
×
80
        {}
×
81
        void DeleteRecords(const std::vector<std::reference_wrapper<TChannelInfo>>& channels, uint32_t count) override
×
82
        {}
×
83
    };
84

85
    class TFakeChannelWriter: public IChannelWriter
86
    {
87
        WBMQTT::Testing::TLoggedFixture& Fixture;
88
        system_clock::time_point StartTime;
89
        std::unique_ptr<TChannelWriter> ChannelWriter;
90

91
    public:
92
        TFakeChannelWriter(WBMQTT::Testing::TLoggedFixture& fixture, system_clock::time_point startTime)
6✔
93
            : Fixture(fixture),
12✔
94
              StartTime(startTime),
6✔
95
              ChannelWriter(std::make_unique<TChannelWriter>())
6✔
96
        {}
6✔
97

98
        void WriteChannel(IStorage& storage,
37✔
99
                          TChannel& channel,
100
                          system_clock::time_point writeTime,
101
                          const std::string& groupName) override
102
        {
103
            Fixture.Emit() << "Write \"" << groupName << "\" " << channel.ChannelInfo->GetName() << " "
37✔
104
                           << duration_cast<milliseconds>(writeTime - StartTime).count();
37✔
105
            Fixture.Emit() << "  Last value: " << channel.LastValue;
37✔
106
            Fixture.Emit() << "  Changed: " << channel.Changed;
37✔
107
            Fixture.Emit() << "  Accumulator value count: " << channel.Accumulator.ValueCount;
37✔
108
            ChannelWriter->WriteChannel(storage, channel, writeTime, groupName);
37✔
109
        }
37✔
110
    };
111
} // namespace
112

113
class TDBLoggerTest: public WBMQTT::Testing::TLoggedFixture
114
{
115
protected:
116
    void StoreByTimeout(system_clock::time_point& systemTime,
31✔
117
                        steady_clock::time_point& steadyTime,
118
                        steady_clock::time_point& startSteadyTime,
119
                        steady_clock::time_point& nextSaveTime,
120
                        const milliseconds& expectedNextSaveTime,
121
                        TMqttDbLoggerMessageHandler& handler)
122
    {
123
        systemTime += nextSaveTime - steadyTime;
31✔
124
        steadyTime = nextSaveTime;
31✔
125
        Emit() << "Store by timeout " << duration_cast<milliseconds>(steadyTime - startSteadyTime).count();
31✔
126
        std::queue<TValueFromMqtt> messages;
31✔
127
        nextSaveTime = handler.HandleMessages(messages, steadyTime, systemTime);
31✔
128
        ASSERT_EQ(duration_cast<milliseconds>(nextSaveTime - startSteadyTime), expectedNextSaveTime);
31✔
129
    }
31✔
130

131
    void StoreByMessage(std::queue<TValueFromMqtt>& messages,
32✔
132
                        const system_clock::time_point& systemTime,
133
                        const steady_clock::time_point& steadyTime,
134
                        const steady_clock::time_point& startSteadyTime,
135
                        steady_clock::time_point& nextSaveTime,
136
                        TMqttDbLoggerMessageHandler& handler)
137
    {
138
        Emit() << "Store by message " << duration_cast<milliseconds>(steadyTime - startSteadyTime).count();
32✔
139
        nextSaveTime = handler.HandleMessages(messages, steadyTime, systemTime);
32✔
140
    }
32✔
141

142
    void StoreByMessage(std::queue<TValueFromMqtt>& messages,
16✔
143
                        const system_clock::time_point& systemTime,
144
                        const steady_clock::time_point& steadyTime,
145
                        const steady_clock::time_point& startSteadyTime,
146
                        steady_clock::time_point& nextSaveTime,
147
                        const milliseconds& expectedNextSaveTime,
148
                        TMqttDbLoggerMessageHandler& handler)
149
    {
150
        StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, handler);
16✔
151
        ASSERT_EQ(duration_cast<milliseconds>(nextSaveTime - startSteadyTime), expectedNextSaveTime);
16✔
152
    }
153
};
154

155
TEST_F(TDBLoggerTest, two_groups)
8✔
156
{
157
    TLoggerCache cache;
2✔
158

159
    TLoggingGroup group;
2✔
160
    group.ChangedInterval = seconds(2);
2✔
161
    group.UnchangedInterval = seconds(3);
2✔
162
    group.ControlPatterns.push_back({"wb-adc", "Vin"});
6✔
163
    group.Name = "most specific";
2✔
164
    cache.Groups.push_back(group);
2✔
165

166
    group.ChangedInterval = seconds(3);
2✔
167
    group.UnchangedInterval = seconds(4);
2✔
168
    group.ControlPatterns.push_back({"wb-adc", "A1"});
6✔
169
    group.Name = "most specific2";
2✔
170
    cache.Groups.push_back(group);
2✔
171

172
    TFakeStorage storage(*this);
2✔
173

174
    auto systemTime = system_clock::now();
2✔
175
    auto steadyTime = steady_clock::now();
2✔
176
    auto startSteadyTime = steadyTime;
2✔
177
    auto startSystemTime = systemTime;
2✔
178
    auto nextSaveTime = steadyTime;
2✔
179

180
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
181

182
    // steadyTime: 0 ms
183
    // most specific: next save by UnchangedInterval = 3000 ms
184
    // most specific2: next save by UnchangedInterval = 4000 ms
185
    handler.Start(startSteadyTime);
2✔
186
    std::queue<TValueFromMqtt> messages;
2✔
187
    messages.push({{"wb-adc", "Vin"}, "12.000", "", 0.0, systemTime});
12✔
188
    // steadyTime: 0 ms
189
    // most specific: next save by UnchangedInterval = 3000 ms
190
    // most specific2: next save by UnchangedInterval = 4000 ms
191
    // Vin: last save = 0 ms
192
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
193

194
    systemTime += milliseconds(100);
2✔
195
    steadyTime += milliseconds(100);
2✔
196
    messages.push({{"wb-adc", "A1"}, "2.000", "", 0.0, systemTime});
12✔
197
    // steadyTime: 100 ms
198
    // most specific: next save by UnchangedInterval = 3000 ms
199
    // most specific2: next save by UnchangedInterval = 4000 ms
200
    // Vin: last save = 0 ms
201
    // A1: last save = 100 ms
202
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
203

204
    systemTime += milliseconds(1000);
2✔
205
    steadyTime += milliseconds(1000);
2✔
206
    messages.push({{"wb-adc", "Vin"}, "13.000", "", 0.0, systemTime});
12✔
207
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
208
    messages.push({{"wb-adc", "A1"}, "3.000", "", 0.0, systemTime});
12✔
209
    messages.push({{"wb-adc", "A1"}, "4.000", "", 0.0, systemTime});
12✔
210
    // steadyTime: 1100 ms
211
    // most specific: next save by UnchangedInterval = 3000 ms
212
    // most specific: next save by ChangedInterval = 2000 ms
213
    // most specific2: next save by UnchangedInterval = 4000 ms
214
    // most specific2: next save by ChangedInterval = 3100 ms
215
    // Vin: last save = 0 ms
216
    // A1: last save = 100 ms
217
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(2000), handler);
2✔
218

219
    // steadyTime: 2000 ms
220
    // most specific: next save by UnchangedInterval = 3000 ms
221
    // most specific2: next save by UnchangedInterval = 4000 ms
222
    // most specific2: next save by ChangedInterval = 3100 ms
223
    // Vin: last save = 2000 ms
224
    // A1: last save = 100 ms
225
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
226

227
    // steadyTime: 3000 ms
228
    // most specific: next save by UnchangedInterval = 6000 ms
229
    // most specific2: next save by UnchangedInterval = 4000 ms
230
    // most specific2: next save by ChangedInterval = 3100 ms
231
    // Vin: last save = 2000 ms
232
    // A1: last save = 100 ms
233
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3100), handler);
2✔
234

235
    // steadyTime: 3100 ms
236
    // most specific: next save by UnchangedInterval = 6000 ms
237
    // most specific2: next save by UnchangedInterval = 4000 ms
238
    // Vin: last save = 2000 ms
239
    // A1: last save = 3100 ms
240
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(4000), handler);
2✔
241

242
    // The values are the same
243
    systemTime = startSystemTime + milliseconds(3500);
2✔
244
    steadyTime = startSteadyTime + milliseconds(3500);
2✔
245
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
246
    messages.push({{"wb-adc", "A1"}, "4.000", "", 0.0, systemTime});
12✔
247
    // steadyTime: 3500 ms
248
    // most specific: next save by UnchangedInterval = 6000 ms
249
    // most specific2: next save by UnchangedInterval = 4000 ms
250
    // Vin: last save = 2000 ms
251
    // A1: last save = 3100 ms
252
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(4000), handler);
2✔
253

254
    // steadyTime: 4000 ms
255
    // most specific: next save by UnchangedInterval = 6000 ms
256
    // most specific2: next save by UnchangedInterval = 8000 ms
257
    // Vin: last save = 4000 ms
258
    // A1: last save = 3100 ms
259
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(6000), handler);
2✔
260

261
    // steadyTime: 6000 ms
262
    // most specific: next save by UnchangedInterval = 9000 ms
263
    // most specific2: next save by UnchangedInterval = 8000 ms
264
    // Vin: last save = 6000 ms
265
    // A1: last save = 3100 ms
266
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(8000), handler);
2✔
267

268
    // steadyTime: 8000 ms
269
    // most specific: next save by UnchangedInterval = 9000 ms
270
    // most specific2: next save by UnchangedInterval = 12000 ms
271
    // Vin: last save = 6000 ms
272
    // A1: last save = 8000 ms
273
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(9000), handler);
2✔
274

275
    // steadyTime: 9000 ms
276
    // most specific: next save by UnchangedInterval = 12000 ms
277
    // most specific2: next save by UnchangedInterval = 12000 ms
278
    // Vin: last save = 6000 ms
279
    // A1: last save = 8000 ms
280
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(12000), handler);
2✔
281

282
    // steadyTime: 12000 ms
283
    // most specific: next save by UnchangedInterval = 15000 ms
284
    // most specific2: next save by UnchangedInterval = 16000 ms
285
    // Vin: last save = 6000 ms
286
    // A1: last save = 8000 ms
287
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(15000), handler);
2✔
288
}
66✔
289

290
TEST_F(TDBLoggerTest, two_overlapping_groups)
8✔
291
{
292
    TLoggerCache cache;
2✔
293

294
    TLoggingGroup group;
2✔
295
    group.ChangedInterval = seconds(2);
2✔
296
    group.UnchangedInterval = seconds(3);
2✔
297
    group.ControlPatterns.push_back({"wb-adc", "Vin"});
6✔
298
    group.Name = "most specific";
2✔
299
    cache.Groups.push_back(group);
2✔
300

301
    group.ChangedInterval = seconds(3);
2✔
302
    group.UnchangedInterval = seconds(4);
2✔
303
    group.ControlPatterns.push_back({"wb-adc", "+"});
6✔
304
    group.Name = "general";
2✔
305
    cache.Groups.push_back(group);
2✔
306

307
    TFakeStorage storage(*this);
2✔
308

309
    auto systemTime = system_clock::now();
2✔
310
    auto steadyTime = steady_clock::now();
2✔
311
    auto startSteadyTime = steadyTime;
2✔
312
    auto startSystemTime = systemTime;
2✔
313
    auto nextSaveTime = steadyTime;
2✔
314

315
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
316

317
    // steadyTime: 0 ms
318
    // most specific: next save by UnchangedInterval = 3000 ms
319
    // general: next save by UnchangedInterval = 4000 ms
320
    handler.Start(startSteadyTime);
2✔
321
    std::queue<TValueFromMqtt> messages;
2✔
322
    messages.push({{"wb-adc", "Vin"}, "12.000", "", 0.0, systemTime});
12✔
323
    // steadyTime: 0 ms
324
    // most specific: next save by UnchangedInterval = 3000 ms
325
    // general: next save by UnchangedInterval = 4000 ms
326
    // Vin: last save = 0 ms
327
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
328

329
    systemTime += milliseconds(100);
2✔
330
    steadyTime += milliseconds(100);
2✔
331
    messages.push({{"wb-adc", "A1"}, "2.000", "", 0.0, systemTime});
12✔
332
    // steadyTime: 100 ms
333
    // most specific: next save by UnchangedInterval = 3000 ms
334
    // general: next save by UnchangedInterval = 4000 ms
335
    // Vin: last save = 0 ms
336
    // A1: last save = 100 ms
337
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
338

339
    systemTime += milliseconds(1000);
2✔
340
    steadyTime += milliseconds(1000);
2✔
341
    messages.push({{"wb-adc", "Vin"}, "13.000", "", 0.0, systemTime});
12✔
342
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
343
    messages.push({{"wb-adc", "A1"}, "3.000", "", 0.0, systemTime});
12✔
344
    messages.push({{"wb-adc", "A1"}, "4.000", "", 0.0, systemTime});
12✔
345
    // steadyTime: 1100 ms
346
    // most specific: next save by UnchangedInterval = 3000 ms
347
    // most specific: next save by ChangedInterval = 2000 ms
348
    // general: next save by UnchangedInterval = 4000 ms
349
    // general: next save by ChangedInterval = 3100 ms
350
    // Vin: last save = 0 ms
351
    // A1: last save = 100 ms
352
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(2000), handler);
2✔
353

354
    // steadyTime: 2000 ms
355
    // most specific: next save by UnchangedInterval = 3000 ms
356
    // general: next save by UnchangedInterval = 4000 ms
357
    // general: next save by ChangedInterval = 3100 ms
358
    // Vin: last save = 2000 ms
359
    // A1: last save = 100 ms
360
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
361

362
    // steadyTime: 3000 ms
363
    // most specific: next save by UnchangedInterval = 6000 ms
364
    // general: next save by UnchangedInterval = 4000 ms
365
    // general: next save by ChangedInterval = 3100 ms
366
    // Vin: last save = 2000 ms
367
    // A1: last save = 100 ms
368
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3100), handler);
2✔
369

370
    // steadyTime: 3100 ms
371
    // most specific: next save by UnchangedInterval = 6000 ms
372
    // general: next save by UnchangedInterval = 4000 ms
373
    // Vin: last save = 2000 ms
374
    // A1: last save = 3100 ms
375
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(4000), handler);
2✔
376

377
    // The values are the same
378
    systemTime = startSystemTime + milliseconds(3500);
2✔
379
    steadyTime = startSteadyTime + milliseconds(3500);
2✔
380
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
381
    messages.push({{"wb-adc", "A1"}, "4.000", "", 0.0, systemTime});
12✔
382
    // steadyTime: 3500 ms
383
    // most specific: next save by UnchangedInterval = 6000 ms
384
    // general: next save by UnchangedInterval = 4000 ms
385
    // Vin: last save = 2000 ms
386
    // A1: last save = 3100 ms
387
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(4000), handler);
2✔
388

389
    // steadyTime: 4000 ms
390
    // most specific: next save by UnchangedInterval = 6000 ms
391
    // general: next save by UnchangedInterval = 8000 ms
392
    // Vin: last save = 4000 ms
393
    // A1: last save = 3100 ms
394
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(6000), handler);
2✔
395

396
    // steadyTime: 6000 ms
397
    // most specific: next save by UnchangedInterval = 9000 ms
398
    // general: next save by UnchangedInterval = 8000 ms
399
    // Vin: last save = 6000 ms
400
    // A1: last save = 3100 ms
401
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(8000), handler);
2✔
402

403
    // steadyTime: 8000 ms
404
    // most specific: next save by UnchangedInterval = 9000 ms
405
    // general: next save by UnchangedInterval = 12000 ms
406
    // Vin: last save = 6000 ms
407
    // A1: last save = 8000 ms
408
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(9000), handler);
2✔
409

410
    // steadyTime: 9000 ms
411
    // most specific: next save by UnchangedInterval = 12000 ms
412
    // general: next save by UnchangedInterval = 12000 ms
413
    // Vin: last save = 6000 ms
414
    // A1: last save = 8000 ms
415
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(12000), handler);
2✔
416

417
    // steadyTime: 12000 ms
418
    // most specific: next save by UnchangedInterval = 15000 ms
419
    // general: next save by UnchangedInterval = 16000 ms
420
    // Vin: last save = 6000 ms
421
    // A1: last save = 8000 ms
422
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(15000), handler);
2✔
423
}
66✔
424

425
TEST_F(TDBLoggerTest, save_precision)
8✔
426
{
427
    TLoggerCache cache;
2✔
428

429
    TLoggingGroup group;
2✔
430
    group.ChangedInterval = seconds(0);
2✔
431
    group.UnchangedInterval = seconds(0);
2✔
432
    group.ControlPatterns.push_back({"+", "+"});
6✔
433
    group.Name = "all";
2✔
434
    cache.Groups.push_back(group);
2✔
435

436
    TFakeStorage storage(*this);
2✔
437
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TChannelWriter>());
2✔
438

439
    auto systemTime = system_clock::time_point();
2✔
440
    auto steadyTime = steady_clock::now();
2✔
441

442
    auto timeStep = milliseconds(100);
2✔
443
    handler.Start(steadyTime);
2✔
444
    std::queue<TValueFromMqtt> messages;
2✔
445
    messages.push({{"wb-adc", "Vin"}, "12.000", "", 0.1, systemTime});
12✔
446
    handler.HandleMessages(messages, steadyTime, systemTime);
2✔
447
    steadyTime += timeStep;
2✔
448
    systemTime += timeStep;
2✔
449
    messages.push({{"wb-adc", "Vin"}, "12.001", "", 0.1, systemTime});
12✔
450
    handler.HandleMessages(messages, steadyTime, systemTime);
2✔
451
    steadyTime += timeStep;
2✔
452
    systemTime += timeStep;
2✔
453
    messages.push({{"wb-adc", "A1"}, "13", "", 0.0, systemTime});
12✔
454
    handler.HandleMessages(messages, steadyTime, systemTime);
2✔
455
    steadyTime += timeStep;
2✔
456
    systemTime += timeStep;
2✔
457
    messages.push({{"wb-adc", "A1"}, "14.001", "", 0.0, systemTime});
12✔
458
    handler.HandleMessages(messages, steadyTime, systemTime);
2✔
459
    steadyTime += timeStep;
2✔
460
    systemTime += timeStep;
2✔
461
}
34✔
462

463
TEST(TPrecisionTest, find_precision)
8✔
464
{
465
    {
466
        TChannel channelData;
2✔
467
        TValueFromMqtt msg;
2✔
468
        msg.Precision = 0.01;
2✔
469
        UpdatePrecision(channelData, msg, true);
2✔
470
        ASSERT_EQ(channelData.Precision, 0.01);
2✔
471
    }
2✔
472
    {
473
        TChannel channelData;
2✔
474
        TValueFromMqtt msg;
2✔
475
        msg.Precision = 0.01;
2✔
476
        UpdatePrecision(channelData, msg, false);
2✔
477
        ASSERT_EQ(channelData.Precision, 0.01);
2✔
478
    }
2✔
479
    {
480
        TChannel channelData;
2✔
481
        TValueFromMqtt msg;
2✔
482
        msg.Value = "100";
2✔
483
        UpdatePrecision(channelData, msg, true);
2✔
484
        ASSERT_EQ(channelData.Precision, 1.0);
2✔
485
    }
2✔
486
    {
487
        TChannel channelData;
2✔
488
        TValueFromMqtt msg;
2✔
489
        msg.Value = "100";
2✔
490
        UpdatePrecision(channelData, msg, false);
2✔
491
        ASSERT_EQ(channelData.Precision, 0.0);
2✔
492
    }
2✔
493
    {
494
        TChannel channelData;
2✔
495
        TValueFromMqtt msg;
2✔
496
        msg.Value = "100.1";
2✔
497
        UpdatePrecision(channelData, msg, true);
2✔
498
        ASSERT_DOUBLE_EQ(channelData.Precision, 0.1);
2✔
499
    }
2✔
500
    {
501
        TChannel channelData;
2✔
502
        TValueFromMqtt msg;
2✔
503
        msg.Value = "100.001";
2✔
504
        UpdatePrecision(channelData, msg, true);
2✔
505
        ASSERT_DOUBLE_EQ(channelData.Precision, 0.001);
2✔
506
    }
2✔
507
}
508

509
TEST_F(TDBLoggerTest, burst)
8✔
510
{
511
    TLoggerCache cache;
2✔
512

513
    TLoggingGroup group;
2✔
514
    group.ChangedInterval = seconds(2);
2✔
515
    group.UnchangedInterval = seconds(3);
2✔
516
    group.MaxBurstRecords = 3;
2✔
517
    group.ControlPatterns.push_back({"+", "+"});
6✔
518
    group.Name = "all";
2✔
519
    cache.Groups.push_back(group);
2✔
520

521
    TFakeStorage storage(*this);
2✔
522

523
    auto systemTime = system_clock::now();
2✔
524
    auto steadyTime = steady_clock::now();
2✔
525
    auto startSteadyTime = steadyTime;
2✔
526
    auto nextSaveTime = steadyTime;
2✔
527

528
    std::queue<TValueFromMqtt> messages;
2✔
529
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
530
    handler.Start(startSteadyTime);
2✔
531

532
    /* Check that BurstRecords remains unchanged on save */
533

534
    messages.push({{"wb-adc", "Vin"}, "12.000", "", 0.0, systemTime});
12✔
535
    // steadyTime: 0 ms
536
    // next save by UnchangedInterval = 3000 ms
537
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
538
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
539

540
    systemTime += milliseconds(1100);
2✔
541
    steadyTime += milliseconds(1100);
2✔
542
    messages.push({{"wb-adc", "Vin"}, "13.000", "", 0.0, systemTime});
12✔
543
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
544
    // steadyTime: 1100 ms
545
    // next save by UnchangedInterval = 3000 ms
546
    // next save by ChangedInterval = 2000 ms
547
    // Vin: last save = 0 ms
548
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(2000), handler);
2✔
549
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
550

551
    /* Check that BurstRecords increases */
552

553
    // steadyTime: 2000 ms
554
    // next save by UnchangedInterval = 3000 ms
555
    // Vin: last save = 2000 ms
556
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
557
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
558

559
    // steadyTime: 3000 ms
560
    // next save by UnchangedInterval = 6000 ms
561
    // Vin: last save = 2000 ms
562
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(6000), handler);
2✔
563
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
564

565
    // steadyTime: 6000 ms
566
    // next save by UnchangedInterval = 9000 ms
567
    // Vin: last save = 2000 ms
568
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(9000), handler);
2✔
569
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 2);
2✔
570

571
    // steadyTime: 9000 ms
572
    // next save by UnchangedInterval = 12000 ms
573
    // Vin: last save = 2000 ms
574
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(12000), handler);
2✔
575
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 3);
2✔
576

577
    /* Check that BurstRecords is not more than limit */
578

579
    // steadyTime: 12000 ms
580
    // next save by UnchangedInterval = 15000 ms
581
    // Vin: last save = 2000 ms
582
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(15000), handler);
2✔
583
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 3);
2✔
584

585
    /* Check unscheduled write */
586

587
    systemTime += milliseconds(2500);
2✔
588
    steadyTime += milliseconds(2500);
2✔
589
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
590
    // steadyTime: 14500 ms
591
    // next save by UnchangedInterval = 15000 ms
592
    // Vin: last save = 14500 ms
593
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(15000), handler);
2✔
594
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 2);
2✔
595

596
    /* BurstRecords = 2 but only one interval without messages is passed. Check
597
     * that BurstRecords is not decreased */
598

599
    // steadyTime: 15000 ms
600
    // next save by UnchangedInterval = 18000 ms
601
    // Vin: last save = 14500 ms
602
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(18000), handler);
2✔
603
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 2);
2✔
604

605
    // steadyTime: 18000 ms
606
    // next save by UnchangedInterval = 21000 ms
607
    // Vin: last save = 14500 ms
608
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(21000), handler);
2✔
609
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 2);
2✔
610

611
    /* Check writing after BurstRecords underflow */
612

613
    systemTime += milliseconds(2500);
2✔
614
    steadyTime += milliseconds(2500);
2✔
615
    messages.push({{"wb-adc", "Vin"}, "11.000", "", 0.0, systemTime});
12✔
616
    messages.push({{"wb-adc", "Vin"}, "12.000", "", 0.0, systemTime});
12✔
617
    messages.push({{"wb-adc", "Vin"}, "13.000", "", 0.0, systemTime});
12✔
618
    messages.push({{"wb-adc", "Vin"}, "14.000", "", 0.0, systemTime});
12✔
619
    messages.push({{"wb-adc", "Vin"}, "15.000", "", 0.0, systemTime});
12✔
620
    // steadyTime: 20500 ms
621
    // next save by UnchangedInterval = 21000 ms
622
    // next save by ChangedInterval = 22500 ms
623
    // Vin: last save = 20500 ms
624
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(21000), handler);
2✔
625
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
626

627
    // steadyTime: 21000 ms
628
    // next save by UnchangedInterval = 24000 ms
629
    // next save by ChangedInterval = 22500 ms
630
    // Vin: last save = 20500 ms
631
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(22500), handler);
2✔
632
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
633

634
    // steadyTime: 22500 ms
635
    // next save by UnchangedInterval = 24000 ms
636
    // Vin: last save = 22500 ms
637
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(24000), handler);
2✔
638
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
639

640
    // steadyTime: 24000 ms
641
    // next save by UnchangedInterval = 27000 ms
642
    // Vin: last save = 22500 ms
643
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(27000), handler);
2✔
644
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
645

646
    /* ADC pattern. One message every 500 ms */
647
    for (size_t i = 0; i < 15; ++i) {
32✔
648
        systemTime += milliseconds(500);
30✔
649
        steadyTime += milliseconds(500);
30✔
650
        messages.push({{"wb-adc", "Vin"}, "11.000", "", 0.0, systemTime});
180✔
651
        StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, handler);
30✔
652
        ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
30✔
653
    }
654
}
194✔
655

656
TEST_F(TDBLoggerTest, burstSwitch)
8✔
657
{
658
    TLoggerCache cache;
2✔
659

660
    TLoggingGroup group;
2✔
661
    group.ChangedInterval = seconds(2);
2✔
662
    group.UnchangedInterval = seconds(3);
2✔
663
    group.MaxBurstRecords = 3;
2✔
664
    group.ControlPatterns.push_back({"+", "+"});
6✔
665
    group.Name = "all";
2✔
666
    cache.Groups.push_back(group);
2✔
667

668
    TFakeStorage storage(*this);
2✔
669

670
    auto systemTime = system_clock::now();
2✔
671
    auto steadyTime = steady_clock::now();
2✔
672
    auto startSteadyTime = steadyTime;
2✔
673
    auto nextSaveTime = steadyTime;
2✔
674

675
    std::queue<TValueFromMqtt> messages;
2✔
676
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
677
    handler.Start(startSteadyTime);
2✔
678

679
    for (size_t i = 0; i < 5; ++i) {
12✔
680
        messages.push({{"wb-adc", "Vin"}, std::to_string(i), "switch", 0.0, systemTime});
60✔
681
    }
682
    // steadyTime: 0 ms
683
    // next save by UnchangedInterval = 3000 ms
684
    // next save by ChangedInterval = 2000 ms
685
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(2000), handler);
2✔
686
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
687

688
    // steadyTime: 2000 ms
689
    // next save by UnchangedInterval = 3000 ms
690
    // Vin: last save = 2000 ms
691
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
692
    ASSERT_EQ(cache.Groups[0].Channels.begin()->second.BurstRecords, 0);
2✔
693
}
32✔
694

695
TEST_F(TDBLoggerTest, burstSwitchAndValue)
8✔
696
{
697
    // If max_burst is non zero, TDBLogger will start writing discrete channels
698
    // before analog channels It will lead to group records count calculation on
699
    // cache with channels with ChannelInfo==nullptr So we will get a segfault The
700
    // test check crash fix
701

702
    TLoggerCache cache;
2✔
703

704
    TLoggingGroup group;
2✔
705
    group.ChangedInterval = seconds(2);
2✔
706
    group.UnchangedInterval = seconds(3);
2✔
707
    group.MaxBurstRecords = 3;
2✔
708
    group.MaxRecords = 100;
2✔
709
    group.ControlPatterns.push_back({"+", "+"});
6✔
710
    group.Name = "all";
2✔
711
    cache.Groups.push_back(group);
2✔
712

713
    TFakeStorage storage(*this);
2✔
714

715
    auto systemTime = system_clock::now();
2✔
716
    auto steadyTime = steady_clock::now();
2✔
717
    auto startSteadyTime = steadyTime;
2✔
718
    auto nextSaveTime = steadyTime;
2✔
719

720
    std::queue<TValueFromMqtt> messages;
2✔
721
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
722
    handler.Start(startSteadyTime);
2✔
723

724
    messages.push({{"wb-adc", "A1"}, "10.0", "", 0.0, systemTime});
12✔
725
    for (size_t i = 0; i < 5; ++i) {
12✔
726
        messages.push({{"wb-adc", "Vin"}, "1", "switch", 0.0, systemTime});
70✔
727
    }
728
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, handler);
2✔
729
    // Must not crash
730
}
50✔
731

732
TEST_F(TDBLoggerTest, several_unchanged)
8✔
733
{
734
    TLoggerCache cache;
2✔
735

736
    TLoggingGroup group;
2✔
737
    group.ChangedInterval = seconds(2);
2✔
738
    group.UnchangedInterval = seconds(3);
2✔
739
    group.MaxBurstRecords = 3;
2✔
740
    group.ControlPatterns.push_back({"+", "+"});
6✔
741
    group.Name = "all";
2✔
742
    cache.Groups.push_back(group);
2✔
743

744
    TFakeStorage storage(*this);
2✔
745

746
    auto systemTime = system_clock::now();
2✔
747
    auto steadyTime = steady_clock::now();
2✔
748
    auto startSteadyTime = steadyTime;
2✔
749
    auto nextSaveTime = steadyTime;
2✔
750

751
    std::queue<TValueFromMqtt> messages;
2✔
752
    TMqttDbLoggerMessageHandler handler(cache, storage, std::make_unique<TFakeChannelWriter>(*this, systemTime));
2✔
753
    handler.Start(startSteadyTime);
2✔
754

755
    messages.push({{"wb-adc", "Vin0"}, "12.000", "", 0.0, systemTime});
12✔
756
    messages.push({{"wb-adc", "Vin1"}, "0.000", "", 0.0, systemTime});
12✔
757
    messages.push({{"wb-adc", "Vin2"}, "0.000", "", 0.0, systemTime});
12✔
758
    // steadyTime: 0 ms
759
    // next save by UnchangedInterval = 3000 ms
760
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
761

762
    systemTime += milliseconds(1100);
2✔
763
    steadyTime += milliseconds(1100);
2✔
764
    messages.push({{"wb-adc", "Vin0"}, "13.000", "", 0.0, systemTime});
12✔
765
    messages.push({{"wb-adc", "Vin1"}, "0.000", "", 0.0, systemTime});
12✔
766
    messages.push({{"wb-adc", "Vin2"}, "0.000", "", 0.0, systemTime});
12✔
767
    // steadyTime: 1100 ms
768
    // next save by UnchangedInterval = 3000 ms
769
    // next save by ChangedInterval = 2000 ms
770
    // Vin: last save = 0 ms
771
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(2000), handler);
2✔
772

773
    systemTime += milliseconds(900);
2✔
774
    steadyTime += milliseconds(900);
2✔
775
    // steadyTime: 2000 ms
776
    // save by ChangedInterval = 2000 ms
777
    // next save by UnchangedInterval = 3000 ms
778
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(3000), handler);
2✔
779

780
    systemTime += milliseconds(1000);
2✔
781
    steadyTime += milliseconds(1000);
2✔
782
    // steadyTime: 3000 ms
783
    // save by UnchangedInterval = 3000 ms
784
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(6000), handler);
2✔
785

786
    systemTime += milliseconds(100);
2✔
787
    steadyTime += milliseconds(100);
2✔
788
    messages.push({{"wb-adc", "Vin0"}, "14.000", "", 0.0, systemTime});
12✔
789
    messages.push({{"wb-adc", "Vin1"}, "0.000", "", 0.0, systemTime});
12✔
790
    messages.push({{"wb-adc", "Vin2"}, "0.000", "", 0.0, systemTime});
12✔
791
    // steadyTime: 3100 ms
792
    // next save by ChangedInterval = 4000 ms
793
    StoreByMessage(messages, systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(4000), handler);
2✔
794

795
    systemTime += milliseconds(900);
2✔
796
    steadyTime += milliseconds(900);
2✔
797
    // steadyTime: 4000 ms
798
    // save by ChangedInterval = 4000 ms
799
    // next save by UnchangedInterval = 6000 ms
800
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(6000), handler);
2✔
801

802
    systemTime += milliseconds(2000);
2✔
803
    steadyTime += milliseconds(2000);
2✔
804
    // steadyTime: 6000 ms
805
    // save by UnchangedInterval = 6000 ms
806
    StoreByTimeout(systemTime, steadyTime, startSteadyTime, nextSaveTime, milliseconds(9000), handler);
2✔
807
}
74✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc