Skip to content

Commit ff40118

Browse files
committed
Add support for most API calls
1 parent 41ce47f commit ff40118

File tree

12 files changed

+335
-27
lines changed

12 files changed

+335
-27
lines changed

mocking/include/cppkafka/mocking/consumer_mock.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include <condition_variable>
1313
#include <queue>
1414
#include <chrono>
15-
#include <boost/optional.hpp>
1615
#include <cppkafka/mocking/handle_mock.h>
1716
#include <cppkafka/mocking/configuration_mock.h>
1817
#include <cppkafka/mocking/topic_partition_mock.h>
@@ -32,12 +31,13 @@ class ConsumerMock : public HandleMock {
3231
~ConsumerMock();
3332

3433
void subscribe(const std::vector<std::string>& topics);
34+
void unsubscribe();
3535
void assign(const std::vector<TopicPartitionMock>& topic_partitions);
3636
void unassign();
37-
void set_opaque(void* opaque);
3837
void pause_partitions(const std::vector<TopicPartitionMock>& topic_partitions);
3938
void resume_partitions(const std::vector<TopicPartitionMock>& topic_partitions);
40-
boost::optional<MessageHandle> poll(std::chrono::milliseconds timeout);
39+
std::unique_ptr<MessageHandle> poll(std::chrono::milliseconds timeout);
40+
std::vector<TopicPartitionMock> get_assignment() const;
4141
private:
4242
static uint64_t make_consumer_id();
4343

mocking/include/cppkafka/mocking/event_processor.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <queue>
77
#include <condition_variable>
88
#include <memory>
9+
#include <chrono>
910
#include <cppkafka/mocking/events/event_base.h>
1011

1112
namespace cppkafka {
@@ -21,12 +22,15 @@ class EventProcessor {
2122
~EventProcessor();
2223

2324
void add_event(EventPtr event);
25+
size_t get_event_count() const;
26+
bool wait_until_empty(std::chrono::milliseconds timeout);
2427
private:
2528
void process_events();
2629

2730
std::thread processing_thread_;
28-
std::mutex events_mutex_;
29-
std::condition_variable events_condition_;
31+
mutable std::mutex events_mutex_;
32+
std::condition_variable new_events_condition_;
33+
std::condition_variable no_events_condition_;
3034
std::queue<EventPtr> events_;
3135
bool running_{true};
3236
};

mocking/include/cppkafka/mocking/handle_mock.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ class HandleMock {
1919
HandleMock(EventProcessorPtr processor, ClusterPtr cluster);
2020
virtual ~HandleMock() = default;
2121

22+
void* get_opaque() const;
23+
size_t get_event_count() const;
2224
void set_cluster(ClusterPtr cluster);
25+
void set_opaque(void* opaque);
2326
protected:
2427
using EventPtr = EventProcessor::EventPtr;
2528

@@ -30,9 +33,11 @@ class HandleMock {
3033
void generate_event(Args&&... args) {
3134
generate_event(EventPtr(new T(cluster_, std::forward<Args>(args)...)));
3235
}
36+
EventProcessor& get_event_processor();
3337
private:
3438
EventProcessorPtr processor_;
3539
ClusterPtr cluster_;
40+
void* opaque_;
3641
};
3742

3843
} // mocking

mocking/include/cppkafka/mocking/handle_wrapper.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,23 @@ class HandleWrapper {
2727

2828
}
2929

30-
31-
3230
T& get_handle() {
3331
return *handle_;
3432
}
3533

3634
const T& get_handle() const {
3735
return *handle_;
3836
}
37+
38+
template <typename U>
39+
U& get_handle() {
40+
return static_cast<U&>(get_handle());
41+
}
42+
43+
template <typename U>
44+
const U& get_handle() const {
45+
return static_cast<U&>(get_handle());
46+
}
3947
private:
4048
std::unique_ptr<T> handle_;
4149
};

mocking/include/cppkafka/mocking/message_handle.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace cppkafka {
1010
namespace mocking {
1111

1212
class KafkaMessageMock;
13+
class MessageHandle;
1314

1415
class MessageHandlePrivateData {
1516
public:
@@ -18,9 +19,14 @@ class MessageHandlePrivateData {
1819

1920
rd_kafka_timestamp_type_t get_timestamp_type() const;
2021
int64_t get_timestamp() const;
22+
MessageHandle* get_owner() const;
23+
void set_owner(MessageHandle* handle);
24+
void set_opaque(void* opaque);
2125
private:
2226
rd_kafka_timestamp_type_t timestamp_type_;
2327
int64_t timestamp_;
28+
MessageHandle* owner_{nullptr};
29+
void* opaque_;
2430
};
2531

2632
class MessageHandle {
@@ -38,6 +44,7 @@ class MessageHandle {
3844
~MessageHandle();
3945

4046
const TopicHandle& get_topic() const;
47+
rd_kafka_message_t& get_message();
4148
const rd_kafka_message_t& get_message() const;
4249
KafkaMessageMock make_message_mock() const;
4350
private:

mocking/include/cppkafka/mocking/producer_mock.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ class ProducerMock : public HandleMock {
1414

1515
ProducerMock(ConfigurationMock config, EventProcessorPtr processor, ClusterPtr cluster);
1616

17-
void produce_message(MessageHandle message_handle);
17+
void produce(MessageHandle message_handle);
18+
bool flush(std::chrono::milliseconds timeout);
19+
size_t poll(std::chrono::milliseconds timeout);
1820
private:
1921
ConfigurationMock config_;
2022
};

mocking/src/api.cpp

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
#include <string>
22
#include <algorithm>
33
#include <cstring>
4+
#include <cstdarg>
45
#include <cppkafka/mocking/api.h>
56
#include <cppkafka/mocking/producer_mock.h>
7+
#include <cppkafka/mocking/consumer_mock.h>
68
#include <cppkafka/mocking/kafka_cluster_registry.h>
79

810
using std::string;
911
using std::copy;
12+
using std::vector;
1013
using std::strlen;
14+
using std::move;
1115
using std::make_shared;
16+
using std::unique_ptr;
17+
18+
using std::chrono::milliseconds;
1219

1320
using namespace cppkafka::mocking;
1421
using namespace cppkafka::mocking::detail;
@@ -205,6 +212,25 @@ rd_kafka_topic_partition_list_add(rd_kafka_topic_partition_list_t* toppar_list,
205212
return output;
206213
}
207214

215+
// rd_kafka_topic_t
216+
217+
rd_kafka_topic_t* rd_kafka_topic_new(rd_kafka_t* rk, const char* topic,
218+
rd_kafka_topic_conf_t* conf) {
219+
return reinterpret_cast<rd_kafka_topic_t*>(new TopicHandle(topic, nullptr));
220+
}
221+
222+
const char* rd_kafka_topic_name(const rd_kafka_topic_t* rkt) {
223+
return reinterpret_cast<const TopicHandle*>(rkt)->get_topic().c_str();
224+
}
225+
226+
void rd_kafka_topic_destroy(rd_kafka_topic_t* rkt) {
227+
delete reinterpret_cast<TopicHandle*>(rkt);
228+
}
229+
230+
int rd_kafka_topic_partition_available(const rd_kafka_topic_t* rkt, int32_t partition) {
231+
return 1;
232+
}
233+
208234
// rd_kafka_t
209235

210236
rd_kafka_t* rd_kafka_new(rd_kafka_type_t type, rd_kafka_conf_t *conf_ptr,
@@ -219,5 +245,195 @@ rd_kafka_t* rd_kafka_new(rd_kafka_type_t type, rd_kafka_conf_t *conf_ptr,
219245
return new rd_kafka_t(new ProducerMock(conf, make_shared<EventProcessor>(),
220246
move(cluster)));
221247
}
248+
else if (type == RD_KAFKA_CONSUMER) {
249+
if (!conf.has_key("group.id")) {
250+
const string error = "Local: Unknown topic";
251+
if (error.size() < errstr_size) {
252+
copy(error.begin(), error.end(), errstr);
253+
errstr[error.size()] = 0;
254+
}
255+
return nullptr;
256+
}
257+
return new rd_kafka_t(new ConsumerMock(conf, make_shared<EventProcessor>(),
258+
move(cluster)));
259+
}
222260
return nullptr;
223261
}
262+
263+
void rd_kafka_destroy(rd_kafka_t* rk) {
264+
delete &rk->get_handle();
265+
}
266+
267+
int rd_kafka_brokers_add(rd_kafka_t* rk, const char* brokerlist) {
268+
auto cluster = KafkaClusterRegistry::instance().get_cluster(brokerlist);
269+
if (cluster) {
270+
rk->get_handle().set_cluster(move(cluster));
271+
}
272+
return 1;
273+
}
274+
275+
const char* rd_kafka_name(const rd_kafka_t* rk) {
276+
return "cppkafka mock handle";
277+
}
278+
279+
rd_kafka_message_t* rd_kafka_consumer_poll(rd_kafka_t* rk, int timeout_ms) {
280+
auto& consumer = rk->get_handle<ConsumerMock>();
281+
auto message_ptr = consumer.poll(milliseconds(timeout_ms));
282+
if (!message_ptr) {
283+
return nullptr;
284+
}
285+
else {
286+
return &message_ptr.release()->get_message();
287+
}
288+
}
289+
290+
void rd_kafka_message_destroy(rd_kafka_message_t* rkmessage) {
291+
delete static_cast<MessageHandlePrivateData*>(rkmessage->_private)->get_owner();
292+
}
293+
294+
rd_kafka_resp_err_t rd_kafka_pause_partitions(rd_kafka_t* rk,
295+
rd_kafka_topic_partition_list_t* partitions) {
296+
const vector<TopicPartitionMock> topic_partitions = from_rdkafka_handle(*partitions);
297+
auto& consumer = rk->get_handle<ConsumerMock>();
298+
consumer.pause_partitions(topic_partitions);
299+
return RD_KAFKA_RESP_ERR_NO_ERROR;
300+
}
301+
302+
rd_kafka_resp_err_t rd_kafka_resume_partitions(rd_kafka_t* rk,
303+
rd_kafka_topic_partition_list_t* partitions) {
304+
const vector<TopicPartitionMock> topic_partitions = from_rdkafka_handle(*partitions);
305+
auto& consumer = rk->get_handle<ConsumerMock>();
306+
consumer.resume_partitions(topic_partitions);
307+
return RD_KAFKA_RESP_ERR_NO_ERROR;
308+
}
309+
310+
rd_kafka_resp_err_t rd_kafka_subscribe(rd_kafka_t* rk,
311+
const rd_kafka_topic_partition_list_t* partitions) {
312+
const vector<TopicPartitionMock> topic_partitions = from_rdkafka_handle(*partitions);
313+
vector<string> topics;
314+
for (const TopicPartitionMock& topic_partition : topic_partitions) {
315+
topics.emplace_back(topic_partition.get_topic());
316+
}
317+
auto& consumer = rk->get_handle<ConsumerMock>();
318+
consumer.subscribe(topics);
319+
return RD_KAFKA_RESP_ERR_NO_ERROR;
320+
}
321+
322+
rd_kafka_resp_err_t rd_kafka_unsubscribe(rd_kafka_t* rk) {
323+
auto& consumer = rk->get_handle<ConsumerMock>();
324+
consumer.unsubscribe();
325+
return RD_KAFKA_RESP_ERR_NO_ERROR;
326+
}
327+
328+
rd_kafka_resp_err_t rd_kafka_assign(rd_kafka_t* rk,
329+
const rd_kafka_topic_partition_list_t* partitions) {
330+
const vector<TopicPartitionMock> topic_partitions = from_rdkafka_handle(*partitions);
331+
auto& consumer = rk->get_handle<ConsumerMock>();
332+
consumer.assign(topic_partitions);
333+
return RD_KAFKA_RESP_ERR_NO_ERROR;
334+
}
335+
336+
rd_kafka_resp_err_t rd_kafka_assignment(rd_kafka_t* rk,
337+
rd_kafka_topic_partition_list_t** partitions) {
338+
auto& consumer = rk->get_handle<ConsumerMock>();
339+
const vector<TopicPartitionMock> assignment = consumer.get_assignment();
340+
*partitions = to_rdkafka_handle(assignment).release();
341+
return RD_KAFKA_RESP_ERR_NO_ERROR;
342+
}
343+
344+
rd_kafka_resp_err_t rd_kafka_flush(rd_kafka_t* rk, int timeout_ms) {
345+
if (rk->get_handle<ProducerMock>().flush(milliseconds(timeout_ms))) {
346+
return RD_KAFKA_RESP_ERR_NO_ERROR;
347+
}
348+
else {
349+
return RD_KAFKA_RESP_ERR__TIMED_OUT;
350+
}
351+
}
352+
353+
int rd_kafka_poll(rd_kafka_t* rk, int timeout_ms) {
354+
return rk->get_handle<ProducerMock>().poll(milliseconds(timeout_ms));
355+
}
356+
357+
rd_kafka_resp_err_t rd_kafka_producev(rd_kafka_t* rk, ...) {
358+
va_list args;
359+
int vtype;
360+
unique_ptr<TopicHandle> topic;
361+
unsigned partition = RD_KAFKA_PARTITION_UA;
362+
void* key_ptr = nullptr;
363+
size_t key_size = 0;
364+
void* payload_ptr = nullptr;
365+
size_t payload_size = 0;
366+
void* opaque = nullptr;
367+
MessageHandle::PointerOwnership ownership = MessageHandle::PointerOwnership::Unowned;
368+
int64_t timestamp = 0;
369+
370+
va_start(args, rk);
371+
while ((vtype = va_arg(args, int)) != RD_KAFKA_VTYPE_END) {
372+
switch (vtype) {
373+
case RD_KAFKA_VTYPE_TOPIC:
374+
topic.reset(new TopicHandle(va_arg(args, const char *), nullptr));
375+
break;
376+
case RD_KAFKA_VTYPE_PARTITION:
377+
partition = va_arg(args, int32_t);
378+
break;
379+
case RD_KAFKA_VTYPE_VALUE:
380+
payload_ptr = va_arg(args, void *);
381+
payload_size = va_arg(args, size_t);
382+
break;
383+
case RD_KAFKA_VTYPE_KEY:
384+
key_ptr = va_arg(args, void *);
385+
key_size = va_arg(args, size_t);
386+
break;
387+
case RD_KAFKA_VTYPE_OPAQUE:
388+
opaque = va_arg(args, void *);
389+
break;
390+
case RD_KAFKA_VTYPE_MSGFLAGS:
391+
if (va_arg(args, int) == static_cast<int>(MessageHandle::PointerOwnership::Owned)) {
392+
ownership = MessageHandle::PointerOwnership::Owned;
393+
}
394+
break;
395+
case RD_KAFKA_VTYPE_TIMESTAMP:
396+
timestamp = va_arg(args, int64_t);
397+
break;
398+
default:
399+
return RD_KAFKA_RESP_ERR__INVALID_ARG;
400+
}
401+
}
402+
va_end(args);
403+
404+
MessageHandlePrivateData private_data(RD_KAFKA_TIMESTAMP_CREATE_TIME, timestamp);
405+
private_data.set_opaque(opaque);
406+
rk->get_handle<ProducerMock>().produce(MessageHandle(
407+
move(topic),
408+
partition,
409+
-1, // offset
410+
key_ptr, key_size,
411+
payload_ptr, payload_size,
412+
RD_KAFKA_RESP_ERR_NO_ERROR,
413+
private_data,
414+
ownership
415+
));
416+
return RD_KAFKA_RESP_ERR_NO_ERROR;
417+
}
418+
419+
int rd_kafka_outq_len(rd_kafka_t* rk) {
420+
return rk->get_handle<ProducerMock>().get_event_count();
421+
}
422+
423+
void* rd_kafka_opaque(const rd_kafka_t* rk) {
424+
return rk->get_handle().get_opaque();
425+
}
426+
427+
void rd_kafka_set_log_level(rd_kafka_t* /*rk*/, int /*level*/) {
428+
429+
}
430+
431+
// misc
432+
433+
const char* rd_kafka_err2str(rd_kafka_resp_err_t err) {
434+
return "cppkafka mock: error";
435+
}
436+
437+
rd_kafka_resp_err_t rd_kafka_errno2err(int errnox) {
438+
return RD_KAFKA_RESP_ERR_NO_ERROR;
439+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy