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

wirenboard / wb-mqtt-db / 1

09 Oct 2026 07:06AM UTC coverage: 79.788% (-2.2%) from 82.03%
1

Pull #70

github

4957c0
sikmir
Fix boolean value aggregation
Pull Request #70: Fix boolean value aggregation

2465 of 6264 branches covered (39.35%)

87 of 87 new or added lines in 2 files covered. (100.0%)

146 existing lines in 8 files now uncovered.

1654 of 2073 relevant lines covered (79.79%)

20.31 hits per line

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

92.11
/test/rpc.test.cpp
1
#include "config.h"
2
#include "sqlite_storage.h"
3
#include <gtest/gtest.h>
4
#include <stdio.h>
5
#include <time.h>
6
#include <wblib/json_utils.h>
7
#include <wblib/testing/fake_mqtt.h>
8
#include <wblib/testing/testlog.h>
9
#include <wblib/wbmqtt.h>
10

11
namespace
12
{
13
    class TFakeStorage: public IStorage
14
    {
15
        WBMQTT::Testing::TLoggedFixture& Fixture;
16
        int Id;
17
        PChannelInfo VinChannel;
18
        PChannelInfo A1Channel;
19

20
    public:
21
        TFakeStorage(WBMQTT::Testing::TLoggedFixture& fixture): Fixture(fixture), Id(0)
7 ✔
22
        {
23
            VinChannel = TFakeStorage::CreateChannel({"wb-adc", "Vin"});
28 !
24
            A1Channel = TFakeStorage::CreateChannel({"wb-adc", "A1"});
21 !
25
        }
7 ✔
26

27
        PChannelInfo CreateChannel(const TChannelName& channelName) override
15 ✔
28
        {
29
            return CreateChannelPrivate(++Id, channelName.Device, channelName.Control);
15 ✔
30
        }
31

32
        void SetChannelPrecision(TChannelInfo& channelInfo, double precision) override
3 ✔
33
        {
34
            SetPrecision(channelInfo, precision);
3 ✔
35
        }
3 ✔
36

37
        void GetRecordsWithAveragingInterval(IRecordsVisitor& visitor,
4 ✔
38
                                             const std::vector<TChannelName>& channels,
39
                                             std::chrono::system_clock::time_point startTime,
40
                                             std::chrono::system_clock::time_point endTime,
41
                                             int64_t startId,
42
                                             uint32_t maxRecords,
43
                                             std::chrono::milliseconds minInterval) override
44
        {
45
            Fixture.Emit() << "Storage GetRecords";
4 !
46
            for (const auto& channel: channels) {
12 ✔
47
                Fixture.Emit() << "  " << channel;
8 !
48
            }
49
            auto t1 = std::chrono::system_clock::to_time_t(startTime);
4 ✔
50
            auto t2 = std::chrono::system_clock::to_time_t(endTime);
4 ✔
51
            Fixture.Emit() << "  " << std::put_time(std::gmtime(&t1), "%Y-%m-%d %X") << " - "
4 !
52
                           << std::put_time(std::gmtime(&t2), "%Y-%m-%d %X");
4 !
53
            Fixture.Emit() << "  from " << startId;
4 !
54
            Fixture.Emit() << "  maxRecords " << maxRecords;
4 !
55
            Fixture.Emit() << "  minInterval " << minInterval.count() << " ms";
4 !
56
            tm dt;
57
            memset(&dt, 0, sizeof(tm));
4 ✔
58
            dt.tm_year = 100;
4 ✔
59
            dt.tm_mon = 3;
4 ✔
60
            dt.tm_mday = 1;
4 ✔
61
            dt.tm_hour = 10;
4 ✔
62
            dt.tm_min = 20;
4 ✔
63
            dt.tm_sec = 30;
4 ✔
64
            auto time = std::chrono::system_clock::from_time_t(timegm(&dt));
4 ✔
65
            visitor.ProcessRecord(1, *VinChannel, "test1", time, false);
8 !
66
            visitor.ProcessRecord(2, *VinChannel, 10.0, time + std::chrono::milliseconds(250), 20.0, 30.0, true);
4 !
67
        }
4 ✔
68

69
        void GetRecordsWithLimit(IRecordsVisitor& visitor,
1 ✔
70
                                 const std::vector<TChannelName>& channels,
71
                                 std::chrono::system_clock::time_point startTime,
72
                                 std::chrono::system_clock::time_point endTime,
73
                                 int64_t startId,
74
                                 uint32_t maxRecords,
75
                                 size_t overallRecordsLimit) override
76
        {
77
            Fixture.Emit() << "Storage GetRecords";
1 !
78
            for (const auto& channel: channels) {
3 ✔
79
                Fixture.Emit() << "  " << channel;
2 !
80
            }
81
            auto t1 = std::chrono::system_clock::to_time_t(startTime);
1 ✔
82
            auto t2 = std::chrono::system_clock::to_time_t(endTime);
1 ✔
83
            Fixture.Emit() << "  " << std::put_time(std::gmtime(&t1), "%Y-%m-%d %X") << " - "
1 !
84
                           << std::put_time(std::gmtime(&t2), "%Y-%m-%d %X");
1 !
85
            Fixture.Emit() << "  from " << startId;
1 !
86
            Fixture.Emit() << "  maxRecords " << maxRecords;
1 !
87
            Fixture.Emit() << "  overallRecordsLimit " << overallRecordsLimit;
1 !
88
            tm dt;
89
            memset(&dt, 0, sizeof(tm));
1 ✔
90
            dt.tm_year = 100;
1 ✔
91
            dt.tm_mon = 3;
1 ✔
92
            dt.tm_mday = 1;
1 ✔
93
            dt.tm_hour = 10;
1 ✔
94
            dt.tm_min = 20;
1 ✔
95
            dt.tm_sec = 30;
1 ✔
96
            visitor.ProcessRecord(1, *VinChannel, "test1", std::chrono::system_clock::from_time_t(timegm(&dt)), false);
2 !
97
            visitor.ProcessRecord(2,
1 !
98
                                  *VinChannel,
1 ✔
99
                                  10.0,
100
                                  std::chrono::system_clock::from_time_t(timegm(&dt)),
101
                                  20.0,
102
                                  30.0,
103
                                  true);
104
        }
1 ✔
105

106
        void GetChannels(IChannelVisitor& visitor) override
1 ✔
107
        {
108
            tm dt;
109
            memset(&dt, 0, sizeof(tm));
1 ✔
110

111
            SetRecordCount(*VinChannel, 100);
1 !
112
            dt.tm_year = 100;
1 ✔
113
            dt.tm_mon = 3;
1 ✔
114
            dt.tm_mday = 1;
1 ✔
115
            dt.tm_hour = 10;
1 ✔
116
            dt.tm_min = 20;
1 ✔
117
            dt.tm_sec = 30;
1 ✔
118
            SetLastRecordTime(*VinChannel, std::chrono::system_clock::from_time_t(timegm(&dt)));
1 !
119
            visitor.ProcessChannel(VinChannel);
1 !
120

121
            SetRecordCount(*A1Channel, 1000);
1 !
122
            dt.tm_year = 110;
1 ✔
123
            dt.tm_mon = 4;
1 ✔
124
            dt.tm_mday = 2;
1 ✔
125
            dt.tm_hour = 11;
1 ✔
126
            dt.tm_min = 21;
1 ✔
127
            dt.tm_sec = 31;
1 ✔
128
            SetLastRecordTime(*A1Channel, std::chrono::system_clock::from_time_t(timegm(&dt)));
1 !
129
            visitor.ProcessChannel(A1Channel);
1 !
130
        }
1 ✔
131

132
        void WriteChannel(TChannelInfo& channelInfo,
×
133
                          const std::string& value,
134
                          const std::string& minimum,
135
                          const std::string& maximum,
136
                          bool retained,
137
                          std::chrono::system_clock::time_point time) override
UNCOV
138
        {}
×
139

140
        void Commit() override
×
UNCOV
141
        {}
×
142
        void DeleteRecords(TChannelInfo& channel, uint32_t count) override
×
UNCOV
143
        {}
×
144
        void DeleteRecords(const std::vector<std::reference_wrapper<TChannelInfo>>& channels, uint32_t count) override
×
UNCOV
145
        {}
×
146
    };
147

148
    class TFakeMqttRpcServer: public WBMQTT::TMqttRpcServer
149
    {
150
        WBMQTT::Testing::TLoggedFixture& Fixture;
151
        std::map<std::string, WBMQTT::TMqttRpcServer::TMethodHandler> Handlers;
152

153
    public:
154
        TFakeMqttRpcServer(WBMQTT::Testing::TLoggedFixture& fixture): Fixture(fixture)
6 ✔
155
        {}
6 ✔
156

157
        void RegisterMethod(const std::string& service,
12 ✔
158
                            const std::string& method,
159
                            WBMQTT::TMqttRpcServer::TMethodHandler handler)
160
        {
161
            Fixture.Emit() << "Register RPC " << service << " " << method;
12 !
162
            Handlers[method] = handler;
12 ✔
163
        }
12 ✔
164

165
        void RegisterAsyncMethod(const std::string& service,
×
166
                                 const std::string& method,
167
                                 WBMQTT::TMqttRpcServer::TAsyncMethodHandler handler)
UNCOV
168
        {}
×
169

170
        void CallRpc(const std::string& method, const std::string& args)
6 ✔
171
        {
172
            Json::Value argsJson;
6 !
173
            {
174
                std::stringstream ss;
6 !
175
                ss << args;
6 !
176
                Json::CharReaderBuilder builder;
6 !
177
                Json::String errs;
6 ✔
178
                // Report failures and their locations in the document.
179
                if (!parseFromStream(builder, ss, &argsJson, &errs)) {
6 !
180
                    throw std::runtime_error("Failed to parse JSON:" + errs);
×
181
                }
182

183
                Fixture.Emit() << "RPC call " << method;
6 !
184
                Fixture.Emit() << "Arguments";
6 !
185
                Fixture.Emit() << args;
6 !
186
            }
6 ✔
187

188
            auto res = Handlers.at(method)(argsJson);
6 !
189

190
            std::stringstream ss;
6 !
191
            Json::StreamWriterBuilder writerBuilder;
6 !
192
            writerBuilder["indentation"] = "  ";
18 !
193
            std::unique_ptr<Json::StreamWriter> writer(writerBuilder.newStreamWriter());
6 !
194
            writer->write(res, &ss);
6 !
195
            Fixture.Emit() << "Response";
6 !
196
            Fixture.Emit() << ss.str();
6 !
197
        }
6 ✔
198

199
        void Start()
×
UNCOV
200
        {}
×
201
        void Stop()
×
UNCOV
202
        {}
×
203
    };
204
} // namespace
205

206
class TRpcTest: public WBMQTT::Testing::TLoggedFixture
207
{
208
protected:
209
    std::string testRootDir;
210
    std::string schemaFile;
211

212
    void SetUp()
7 ✔
213
    {
214
        char* d = getenv("TEST_DIR_ABS");
7 ✔
215
        if (d != NULL) {
7 !
216
            testRootDir = d;
7 ✔
217
            testRootDir += '/';
7 ✔
218
        }
219
        testRootDir += "dblogger_test_data";
7 ✔
220

221
        schemaFile = testRootDir + "/../../wb-mqtt-db.schema.json";
7 !
222
    }
7 ✔
223
};
224

225
TEST_F(TRpcTest, get_channels)
8 ✔
226
{
227
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
228
    TFakeMqttRpcServer rpc(*this);
2 ✔
229
    TFakeStorage storage(*this);
2 !
230
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
231
    handler.Register(rpc);
2 !
232
    rpc.CallRpc("get_channels", "{\"id\":1}");
6 !
233
}
2 ✔
234

235
TEST_F(TRpcTest, get_records_v0)
8 ✔
236
{
237
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
238
    TFakeMqttRpcServer rpc(*this);
2 ✔
239
    TFakeStorage storage(*this);
2 !
240
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
241
    handler.Register(rpc);
2 !
242
    rpc.CallRpc("get_values",
6 !
243
                "{\"channels\":[[\"wb-adc\",\"Vin\"],[\"wb-adc\",\"A1\"]],"
244
                "\"ver\":0,\"timestamp\":{\"lt\":954566430}}");
245
}
2 ✔
246

247
TEST_F(TRpcTest, get_records_v1)
8 ✔
248
{
249
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
250
    TFakeMqttRpcServer rpc(*this);
2 ✔
251
    TFakeStorage storage(*this);
2 !
252
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
253
    handler.Register(rpc);
2 !
254
    rpc.CallRpc("get_values",
6 !
255
                "{\"channels\":[[\"wb-adc\",\"Vin\"],[\"wb-adc\",\"A1\"]],"
256
                "\"ver\":1,\"timestamp\":{\"lt\":954566430}}");
257
}
2 ✔
258

259
TEST_F(TRpcTest, get_records_v1_min_interval)
8 ✔
260
{
261
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
262
    TFakeMqttRpcServer rpc(*this);
2 ✔
263
    TFakeStorage storage(*this);
2 !
264
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
265
    handler.Register(rpc);
2 !
266
    rpc.CallRpc("get_values",
6 !
267
                "{\"channels\":[[\"wb-adc\",\"Vin\"],[\"wb-adc\",\"A1\"]],\"ver\":1,"
268
                "\"timestamp\":{\"lt\":954566430}, \"min_interval\": 95040.00000000001}");
269
}
2 ✔
270

271
TEST_F(TRpcTest, get_records_v1_max_records)
8 ✔
272
{
273
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
274
    TFakeMqttRpcServer rpc(*this);
2 ✔
275
    TFakeStorage storage(*this);
2 !
276
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
277
    handler.Register(rpc);
2 !
278
    rpc.CallRpc("get_values",
6 !
279
                "{\"channels\":[[\"wb-adc\",\"Vin\"],[\"wb-adc\",\"A1\"]],"
280
                "\"ver\":1,\"timestamp\":{\"lt\":954566430}, \"max_records\": "
281
                "100, \"min_interval\": 95040.00000000001}");
282
}
2 ✔
283

284
TEST_F(TRpcTest, get_records_v1_with_milliseconds)
8 ✔
285
{
286
    TLoggerCache cache(LoadConfig(testRootDir + "/wb-mqtt-db.conf", schemaFile).Cache);
2 !
287
    TFakeMqttRpcServer rpc(*this);
2 ✔
288
    TFakeStorage storage(*this);
2 !
289
    TMQTTDBLoggerRpcHandler handler(cache, storage, std::chrono::seconds(5));
2 !
290
    handler.Register(rpc);
2 !
291
    rpc.CallRpc("get_values",
6 !
292
                "{\"channels\":[[\"wb-adc\",\"Vin\"],[\"wb-adc\",\"A1\"]],"
293
                "\"ver\":1,\"timestamp\":{\"lt\":954566430}, \"min_interval\": "
294
                "95040, \"with_milliseconds\": true}");
295
}
2 ✔
296

297
TEST_F(TRpcTest, round)
8 ✔
298
{
299
    TFakeStorage storage(*this);
2 !
300
    auto channel = storage.CreateChannel({"wb-adc", "A2"});
6 !
301
    TJsonRecordsVisitor visitor(1, 100, std::chrono::seconds(1));
2 !
302
    visitor.ProcessRecord(1, *channel, "10.001", std::chrono::system_clock::time_point(), false);
4 !
303
    storage.SetChannelPrecision(*channel, 1);
2 !
304
    visitor.ProcessRecord(2, *channel, "10.001", std::chrono::system_clock::time_point(), false);
4 !
305
    visitor.ProcessRecord(3, *channel, 10.001, std::chrono::system_clock::time_point(), 1.0, 20.0, false);
2 !
306
    storage.SetChannelPrecision(*channel, 0.01);
2 !
307
    visitor.ProcessRecord(4, *channel, 10.055, std::chrono::system_clock::time_point(), 1.0, 20.012, false);
2 !
308
    storage.SetChannelPrecision(*channel, 0.0);
2 !
309
    visitor.ProcessRecord(4, *channel, 10.055, std::chrono::system_clock::time_point(), 1.0, 20.012, false);
2 !
310

311
    Json::StreamWriterBuilder writerBuilder;
2 !
312
    writerBuilder["indentation"] = "  ";
6 !
313
    std::unique_ptr<Json::StreamWriter> writer(writerBuilder.newStreamWriter());
2 !
314
    std::stringstream ss;
2 !
315
    writer->write(visitor.Root, &ss);
2 !
316
    Emit() << ss.str();
2 !
317
}
2 ✔
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