• 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

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
138
        {}
×
139

140
        void Commit() override
×
141
        {}
×
142
        void DeleteRecords(TChannelInfo& channel, uint32_t count) override
×
143
        {}
×
144
        void DeleteRecords(const std::vector<std::reference_wrapper<TChannelInfo>>& channels, uint32_t count) override
×
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)
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()
×
200
        {}
×
201
        void Stop()
×
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