summaryrefslogtreecommitdiffstats
path: root/exporting/tests
diff options
context:
space:
mode:
Diffstat (limited to 'exporting/tests')
-rw-r--r--exporting/tests/Makefile.am4
-rw-r--r--exporting/tests/exporting_doubles.c395
-rw-r--r--exporting/tests/exporting_fixtures.c163
-rw-r--r--exporting/tests/netdata_doubles.c247
-rw-r--r--exporting/tests/system_doubles.c61
-rw-r--r--exporting/tests/test_exporting_engine.c1939
-rw-r--r--exporting/tests/test_exporting_engine.h203
7 files changed, 3012 insertions, 0 deletions
diff --git a/exporting/tests/Makefile.am b/exporting/tests/Makefile.am
new file mode 100644
index 000000000..babdcf0df
--- /dev/null
+++ b/exporting/tests/Makefile.am
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
diff --git a/exporting/tests/exporting_doubles.c b/exporting/tests/exporting_doubles.c
new file mode 100644
index 000000000..3c73e0327
--- /dev/null
+++ b/exporting/tests/exporting_doubles.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "test_exporting_engine.h"
+
+struct engine *__real_read_exporting_config();
+struct engine *__wrap_read_exporting_config()
+{
+ function_called();
+ return mock_ptr_type(struct engine *);
+}
+
+struct engine *__mock_read_exporting_config()
+{
+ struct engine *engine = calloc(1, sizeof(struct engine));
+ engine->config.hostname = strdupz("test_engine_host");
+ engine->config.update_every = 3;
+
+
+ engine->instance_root = calloc(1, sizeof(struct instance));
+ struct instance *instance = engine->instance_root;
+ instance->engine = engine;
+ instance->config.type = EXPORTING_CONNECTOR_TYPE_GRAPHITE;
+ instance->config.name = strdupz("instance_name");
+ instance->config.destination = strdupz("localhost");
+ instance->config.prefix = strdupz("netdata");
+ instance->config.hostname = strdupz("test-host");
+ instance->config.update_every = 1;
+ instance->config.buffer_on_failures = 10;
+ instance->config.timeoutms = 10000;
+ instance->config.charts_pattern = simple_pattern_create("*", NULL, SIMPLE_PATTERN_EXACT);
+ instance->config.hosts_pattern = simple_pattern_create("*", NULL, SIMPLE_PATTERN_EXACT);
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+
+ return engine;
+}
+
+int __real_init_connectors(struct engine *engine);
+int __wrap_init_connectors(struct engine *engine)
+{
+ function_called();
+ check_expected_ptr(engine);
+ return mock_type(int);
+}
+
+int __real_mark_scheduled_instances(struct engine *engine);
+int __wrap_mark_scheduled_instances(struct engine *engine)
+{
+ function_called();
+ check_expected_ptr(engine);
+ return mock_type(int);
+}
+
+calculated_number __real_exporting_calculate_value_from_stored_data(
+ struct instance *instance,
+ RRDDIM *rd,
+ time_t *last_timestamp);
+calculated_number __wrap_exporting_calculate_value_from_stored_data(
+ struct instance *instance,
+ RRDDIM *rd,
+ time_t *last_timestamp)
+{
+ (void)instance;
+ (void)rd;
+
+ *last_timestamp = 15052;
+
+ function_called();
+ return mock_type(calculated_number);
+}
+
+int __real_prepare_buffers(struct engine *engine);
+int __wrap_prepare_buffers(struct engine *engine)
+{
+ function_called();
+ check_expected_ptr(engine);
+ return mock_type(int);
+}
+
+void __wrap_create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_system)
+{
+ function_called();
+ check_expected_ptr(st_rusage);
+ check_expected_ptr(rd_user);
+ check_expected_ptr(rd_system);
+}
+
+void __wrap_send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system)
+{
+ function_called();
+ check_expected_ptr(st_rusage);
+ check_expected_ptr(rd_user);
+ check_expected_ptr(rd_system);
+}
+
+int __wrap_send_internal_metrics(struct instance *instance)
+{
+ function_called();
+ check_expected_ptr(instance);
+ return mock_type(int);
+}
+
+int __wrap_rrdhost_is_exportable(struct instance *instance, RRDHOST *host)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(host);
+ return mock_type(int);
+}
+
+int __wrap_rrdset_is_exportable(struct instance *instance, RRDSET *st)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(st);
+ return mock_type(int);
+}
+
+int __mock_start_batch_formatting(struct instance *instance)
+{
+ function_called();
+ check_expected_ptr(instance);
+ return mock_type(int);
+}
+
+int __mock_start_host_formatting(struct instance *instance, RRDHOST *host)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(host);
+ return mock_type(int);
+}
+
+int __mock_start_chart_formatting(struct instance *instance, RRDSET *st)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(st);
+ return mock_type(int);
+}
+
+int __mock_metric_formatting(struct instance *instance, RRDDIM *rd)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(rd);
+ return mock_type(int);
+}
+
+int __mock_end_chart_formatting(struct instance *instance, RRDSET *st)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(st);
+ return mock_type(int);
+}
+
+int __mock_end_host_formatting(struct instance *instance, RRDHOST *host)
+{
+ function_called();
+ check_expected_ptr(instance);
+ check_expected_ptr(host);
+ return mock_type(int);
+}
+
+int __mock_end_batch_formatting(struct instance *instance)
+{
+ function_called();
+ check_expected_ptr(instance);
+ return mock_type(int);
+}
+
+int __wrap_simple_connector_end_batch(struct instance *instance)
+{
+ function_called();
+ check_expected_ptr(instance);
+ return mock_type(int);
+}
+
+#if ENABLE_PROMETHEUS_REMOTE_WRITE
+void *__wrap_init_write_request()
+{
+ function_called();
+ return mock_ptr_type(void *);
+}
+
+void __wrap_add_host_info(
+ void *write_request_p,
+ const char *name, const char *instance, const char *application, const char *version, const int64_t timestamp)
+{
+ function_called();
+ check_expected_ptr(write_request_p);
+ check_expected_ptr(name);
+ check_expected_ptr(instance);
+ check_expected_ptr(application);
+ check_expected_ptr(version);
+ check_expected(timestamp);
+}
+
+void __wrap_add_label(void *write_request_p, char *key, char *value)
+{
+ function_called();
+ check_expected_ptr(write_request_p);
+ check_expected_ptr(key);
+ check_expected_ptr(value);
+}
+
+void __wrap_add_metric(
+ void *write_request_p,
+ const char *name, const char *chart, const char *family, const char *dimension,
+ const char *instance, const double value, const int64_t timestamp)
+{
+ function_called();
+ check_expected_ptr(write_request_p);
+ check_expected_ptr(name);
+ check_expected_ptr(chart);
+ check_expected_ptr(family);
+ check_expected_ptr(dimension);
+ check_expected_ptr(instance);
+ check_expected(value);
+ check_expected(timestamp);
+}
+#endif // ENABLE_PROMETHEUS_REMOTE_WRITE
+
+#if HAVE_KINESIS
+void __wrap_aws_sdk_init()
+{
+ function_called();
+}
+
+void __wrap_kinesis_init(
+ void *kinesis_specific_data_p, const char *region, const char *access_key_id, const char *secret_key,
+ const long timeout)
+{
+ function_called();
+ check_expected_ptr(kinesis_specific_data_p);
+ check_expected_ptr(region);
+ check_expected_ptr(access_key_id);
+ check_expected_ptr(secret_key);
+ check_expected(timeout);
+}
+
+void __wrap_kinesis_put_record(
+ void *kinesis_specific_data_p, const char *stream_name, const char *partition_key, const char *data,
+ size_t data_len)
+{
+ function_called();
+ check_expected_ptr(kinesis_specific_data_p);
+ check_expected_ptr(stream_name);
+ check_expected_ptr(partition_key);
+ check_expected_ptr(data);
+ check_expected_ptr(data);
+ check_expected(data_len);
+}
+
+int __wrap_kinesis_get_result(void *request_outcomes_p, char *error_message, size_t *sent_bytes, size_t *lost_bytes)
+{
+ function_called();
+ check_expected_ptr(request_outcomes_p);
+ check_expected_ptr(error_message);
+ check_expected_ptr(sent_bytes);
+ check_expected_ptr(lost_bytes);
+ return mock_type(int);
+}
+#endif // HAVE_KINESIS
+
+#if ENABLE_EXPORTING_PUBSUB
+int __wrap_pubsub_init(
+ void *pubsub_specific_data_p, char *error_message, const char *destination, const char *credentials_file,
+ const char *project_id, const char *topic_id)
+{
+ function_called();
+ check_expected_ptr(pubsub_specific_data_p);
+ check_expected_ptr(error_message);
+ check_expected_ptr(destination);
+ check_expected_ptr(credentials_file);
+ check_expected_ptr(project_id);
+ check_expected_ptr(topic_id);
+ return mock_type(int);
+}
+
+int __wrap_pubsub_add_message(void *pubsub_specific_data_p, char *data)
+{
+ function_called();
+ check_expected_ptr(pubsub_specific_data_p);
+ check_expected_ptr(data);
+ return mock_type(int);
+}
+
+int __wrap_pubsub_publish(
+ void *pubsub_specific_data_p, char *error_message, size_t buffered_metrics, size_t buffered_bytes)
+{
+ function_called();
+ check_expected_ptr(pubsub_specific_data_p);
+ check_expected_ptr(error_message);
+ check_expected(buffered_metrics);
+ check_expected(buffered_bytes);
+ return mock_type(int);
+}
+
+int __wrap_pubsub_get_result(
+ void *pubsub_specific_data_p, char *error_message,
+ size_t *sent_metrics, size_t *sent_bytes, size_t *lost_metrics, size_t *lost_bytes)
+{
+ function_called();
+ check_expected_ptr(pubsub_specific_data_p);
+ check_expected_ptr(error_message);
+ check_expected_ptr(sent_metrics);
+ check_expected_ptr(sent_bytes);
+ check_expected_ptr(lost_metrics);
+ check_expected_ptr(lost_bytes);
+ return mock_type(int);
+}
+#endif // ENABLE_EXPORTING_PUBSUB
+
+#if HAVE_MONGOC
+void __wrap_mongoc_init()
+{
+ function_called();
+}
+
+mongoc_uri_t * __wrap_mongoc_uri_new_with_error (const char *uri_string, bson_error_t *error)
+{
+ function_called();
+ check_expected_ptr(uri_string);
+ check_expected_ptr(error);
+ return mock_ptr_type(mongoc_uri_t *);
+}
+
+int32_t __wrap_mongoc_uri_get_option_as_int32(const mongoc_uri_t *uri, const char *option, int32_t fallback)
+{
+ function_called();
+ check_expected_ptr(uri);
+ check_expected_ptr(option);
+ check_expected(fallback);
+ return mock_type(int32_t);
+}
+
+bool __wrap_mongoc_uri_set_option_as_int32 (const mongoc_uri_t *uri, const char *option, int32_t value)
+{
+ function_called();
+ check_expected_ptr(uri);
+ check_expected_ptr(option);
+ check_expected(value);
+ return mock_type(bool);
+}
+
+mongoc_client_t * __wrap_mongoc_client_new_from_uri (const mongoc_uri_t *uri)
+{
+ function_called();
+ check_expected_ptr(uri);
+ return mock_ptr_type(mongoc_client_t *);
+}
+
+bool __wrap_mongoc_client_set_appname (mongoc_client_t *client, const char *appname)
+{
+ function_called();
+ check_expected_ptr(client);
+ check_expected_ptr(appname);
+ return mock_type(bool);
+}
+
+mongoc_collection_t *
+__wrap_mongoc_client_get_collection(mongoc_client_t *client, const char *db, const char *collection)
+{
+ function_called();
+ check_expected_ptr(client);
+ check_expected_ptr(db);
+ check_expected_ptr(collection);
+ return mock_ptr_type(mongoc_collection_t *);
+}
+
+void __wrap_mongoc_uri_destroy (mongoc_uri_t *uri)
+{
+ function_called();
+ check_expected_ptr(uri);
+}
+
+bool __wrap_mongoc_collection_insert_many(
+ mongoc_collection_t *collection,
+ const bson_t **documents,
+ size_t n_documents,
+ const bson_t *opts,
+ bson_t *reply,
+ bson_error_t *error)
+{
+ function_called();
+ check_expected_ptr(collection);
+ check_expected_ptr(documents);
+ check_expected(n_documents);
+ check_expected_ptr(opts);
+ check_expected_ptr(reply);
+ check_expected_ptr(error);
+ return mock_type(bool);
+}
+#endif // HAVE_MONGOC
diff --git a/exporting/tests/exporting_fixtures.c b/exporting/tests/exporting_fixtures.c
new file mode 100644
index 000000000..00bb0ed0f
--- /dev/null
+++ b/exporting/tests/exporting_fixtures.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "test_exporting_engine.h"
+
+int setup_configured_engine(void **state)
+{
+ struct engine *engine = __mock_read_exporting_config();
+ engine->instance_root->data_is_ready = 1;
+
+ *state = engine;
+
+ return 0;
+}
+
+int teardown_configured_engine(void **state)
+{
+ struct engine *engine = *state;
+
+ struct instance *instance = engine->instance_root;
+ free((void *)instance->config.destination);
+ free((void *)instance->config.name);
+ free((void *)instance->config.prefix);
+ free((void *)instance->config.hostname);
+ simple_pattern_free(instance->config.charts_pattern);
+ simple_pattern_free(instance->config.hosts_pattern);
+ free(instance);
+
+ free((void *)engine->config.hostname);
+ free(engine);
+
+ return 0;
+}
+
+int setup_rrdhost()
+{
+ localhost = calloc(1, sizeof(RRDHOST));
+
+ localhost->rrd_update_every = 1;
+
+ localhost->tags = strdupz("TAG1=VALUE1 TAG2=VALUE2");
+
+ struct label *label = calloc(1, sizeof(struct label));
+ label->key = strdupz("key1");
+ label->value = strdupz("value1");
+ label->label_source = LABEL_SOURCE_NETDATA_CONF;
+ localhost->labels.head = label;
+
+ label = calloc(1, sizeof(struct label));
+ label->key = strdupz("key2");
+ label->value = strdupz("value2");
+ label->label_source = LABEL_SOURCE_AUTO;
+ localhost->labels.head->next = label;
+
+ localhost->rrdset_root = calloc(1, sizeof(RRDSET));
+ RRDSET *st = localhost->rrdset_root;
+ st->rrdhost = localhost;
+ strcpy(st->id, "chart_id");
+ st->name = strdupz("chart_name");
+ st->flags |= RRDSET_FLAG_ENABLED;
+ st->rrd_memory_mode |= RRD_MEMORY_MODE_SAVE;
+ st->update_every = 1;
+
+ localhost->rrdset_root->dimensions = calloc(1, sizeof(RRDDIM));
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ rd->rrdset = st;
+ rd->id = strdupz("dimension_id");
+ rd->name = strdupz("dimension_name");
+ rd->last_collected_value = 123000321;
+ rd->last_collected_time.tv_sec = 15051;
+ rd->collections_counter++;
+ rd->next = NULL;
+
+ rd->state = calloc(1, sizeof(*rd->state));
+ rd->state->query_ops.oldest_time = __mock_rrddim_query_oldest_time;
+ rd->state->query_ops.latest_time = __mock_rrddim_query_latest_time;
+ rd->state->query_ops.init = __mock_rrddim_query_init;
+ rd->state->query_ops.is_finished = __mock_rrddim_query_is_finished;
+ rd->state->query_ops.next_metric = __mock_rrddim_query_next_metric;
+ rd->state->query_ops.finalize = __mock_rrddim_query_finalize;
+
+ return 0;
+}
+
+int teardown_rrdhost()
+{
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ free((void *)rd->name);
+ free((void *)rd->id);
+ free(rd->state);
+ free(rd);
+
+ RRDSET *st = localhost->rrdset_root;
+ free((void *)st->name);
+ free(st);
+
+ free(localhost->labels.head->next->key);
+ free(localhost->labels.head->next->value);
+ free(localhost->labels.head->next);
+ free(localhost->labels.head->key);
+ free(localhost->labels.head->value);
+ free(localhost->labels.head);
+
+ free((void *)localhost->tags);
+ free(localhost);
+
+ return 0;
+}
+
+int setup_initialized_engine(void **state)
+{
+ setup_configured_engine(state);
+
+ struct engine *engine = *state;
+ init_connectors_in_tests(engine);
+
+ setup_rrdhost();
+
+ return 0;
+}
+
+int teardown_initialized_engine(void **state)
+{
+ struct engine *engine = *state;
+
+ teardown_rrdhost();
+ buffer_free(engine->instance_root->labels);
+ buffer_free(engine->instance_root->buffer);
+ teardown_configured_engine(state);
+
+ return 0;
+}
+
+int setup_prometheus(void **state)
+{
+ (void)state;
+
+ prometheus_exporter_instance = calloc(1, sizeof(struct instance));
+
+ setup_rrdhost();
+
+ prometheus_exporter_instance->config.update_every = 10;
+
+ prometheus_exporter_instance->config.options |=
+ EXPORTING_OPTION_SEND_NAMES | EXPORTING_OPTION_SEND_CONFIGURED_LABELS | EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ prometheus_exporter_instance->config.charts_pattern = simple_pattern_create("*", NULL, SIMPLE_PATTERN_EXACT);
+ prometheus_exporter_instance->config.hosts_pattern = simple_pattern_create("*", NULL, SIMPLE_PATTERN_EXACT);
+
+ return 0;
+}
+
+int teardown_prometheus(void **state)
+{
+ (void)state;
+
+ teardown_rrdhost();
+
+ simple_pattern_free(prometheus_exporter_instance->config.charts_pattern);
+ simple_pattern_free(prometheus_exporter_instance->config.hosts_pattern);
+ free(prometheus_exporter_instance);
+
+ return 0;
+}
diff --git a/exporting/tests/netdata_doubles.c b/exporting/tests/netdata_doubles.c
new file mode 100644
index 000000000..f4da7769f
--- /dev/null
+++ b/exporting/tests/netdata_doubles.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "test_exporting_engine.h"
+
+// Use memomy allocation functions guarded by CMocka in strdupz
+const char *__wrap_strdupz(const char *s)
+{
+ char *duplicate = malloc(sizeof(char) * (strlen(s) + 1));
+ strcpy(duplicate, s);
+
+ return duplicate;
+}
+
+time_t __wrap_now_realtime_sec(void)
+{
+ function_called();
+ return mock_type(time_t);
+}
+
+void __wrap_uv_thread_set_name_np(uv_thread_t ut, const char* name)
+{
+ (void)ut;
+ (void)name;
+
+ function_called();
+}
+
+void __wrap_info_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
+{
+ (void)file;
+ (void)function;
+ (void)line;
+
+ function_called();
+
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(log_line, MAX_LOG_LINE, fmt, args);
+ va_end(args);
+}
+
+int __wrap_connect_to_one_of(
+ const char *destination,
+ int default_port,
+ struct timeval *timeout,
+ size_t *reconnects_counter,
+ char *connected_to,
+ size_t connected_to_size)
+{
+ (void)timeout;
+
+ function_called();
+
+ check_expected(destination);
+ check_expected_ptr(default_port);
+ // TODO: check_expected_ptr(timeout);
+ check_expected(reconnects_counter);
+ check_expected(connected_to);
+ check_expected(connected_to_size);
+
+ return mock_type(int);
+}
+
+void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line)
+{
+ (void)host;
+ (void)file;
+ (void)function;
+ (void)line;
+}
+
+void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line)
+{
+ (void)st;
+ (void)file;
+ (void)function;
+ (void)line;
+}
+
+void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line)
+{
+ (void)file;
+ (void)function;
+ (void)line;
+}
+
+RRDSET *rrdset_create_custom(
+ RRDHOST *host,
+ const char *type,
+ const char *id,
+ const char *name,
+ const char *family,
+ const char *context,
+ const char *title,
+ const char *units,
+ const char *plugin,
+ const char *module,
+ long priority,
+ int update_every,
+ RRDSET_TYPE chart_type,
+ RRD_MEMORY_MODE memory_mode,
+ long history_entries)
+{
+ check_expected_ptr(host);
+ check_expected_ptr(type);
+ check_expected_ptr(id);
+ check_expected_ptr(name);
+ check_expected_ptr(family);
+ check_expected_ptr(context);
+ UNUSED(title);
+ check_expected_ptr(units);
+ check_expected_ptr(plugin);
+ check_expected_ptr(module);
+ check_expected(priority);
+ check_expected(update_every);
+ check_expected(chart_type);
+ UNUSED(memory_mode);
+ UNUSED(history_entries);
+
+ function_called();
+
+ return mock_ptr_type(RRDSET *);
+}
+
+void rrdset_next_usec(RRDSET *st, usec_t microseconds)
+{
+ check_expected_ptr(st);
+ UNUSED(microseconds);
+
+ function_called();
+}
+
+void rrdset_done(RRDSET *st)
+{
+ check_expected_ptr(st);
+
+ function_called();
+}
+
+RRDDIM *rrddim_add_custom(
+ RRDSET *st,
+ const char *id,
+ const char *name,
+ collected_number multiplier,
+ collected_number divisor,
+ RRD_ALGORITHM algorithm,
+ RRD_MEMORY_MODE memory_mode)
+{
+ check_expected_ptr(st);
+ UNUSED(id);
+ check_expected_ptr(name);
+ check_expected(multiplier);
+ check_expected(divisor);
+ check_expected(algorithm);
+ UNUSED(memory_mode);
+
+ function_called();
+
+ return NULL;
+}
+
+collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
+{
+ check_expected_ptr(st);
+ UNUSED(rd);
+ UNUSED(value);
+
+ function_called();
+
+ return 0;
+}
+
+const char *rrd_memory_mode_name(RRD_MEMORY_MODE id)
+{
+ (void)id;
+ return RRD_MEMORY_MODE_NONE_NAME;
+}
+
+calculated_number rrdvar2number(RRDVAR *rv)
+{
+ (void)rv;
+ return 0;
+}
+
+int foreach_host_variable_callback(RRDHOST *host, int (*callback)(RRDVAR *rv, void *data), void *data)
+{
+ (void)host;
+ (void)callback;
+ (void)data;
+ return 0;
+}
+
+void rrdset_update_heterogeneous_flag(RRDSET *st)
+{
+ (void)st;
+}
+
+time_t __mock_rrddim_query_oldest_time(RRDDIM *rd)
+{
+ (void)rd;
+
+ function_called();
+ return mock_type(time_t);
+}
+
+time_t __mock_rrddim_query_latest_time(RRDDIM *rd)
+{
+ (void)rd;
+
+ function_called();
+ return mock_type(time_t);
+}
+
+void __mock_rrddim_query_init(RRDDIM *rd, struct rrddim_query_handle *handle, time_t start_time, time_t end_time)
+{
+ (void)rd;
+ (void)handle;
+
+ function_called();
+ check_expected(start_time);
+ check_expected(end_time);
+}
+
+int __mock_rrddim_query_is_finished(struct rrddim_query_handle *handle)
+{
+ (void)handle;
+
+ function_called();
+ return mock_type(int);
+}
+
+storage_number __mock_rrddim_query_next_metric(struct rrddim_query_handle *handle, time_t *current_time)
+{
+ (void)handle;
+ (void)current_time;
+
+ function_called();
+ return mock_type(storage_number);
+}
+
+void __mock_rrddim_query_finalize(struct rrddim_query_handle *handle)
+{
+ (void)handle;
+
+ function_called();
+}
diff --git a/exporting/tests/system_doubles.c b/exporting/tests/system_doubles.c
new file mode 100644
index 000000000..ca85800c0
--- /dev/null
+++ b/exporting/tests/system_doubles.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "test_exporting_engine.h"
+
+void __wrap_uv_thread_create(uv_thread_t thread, void (*worker)(void *arg), void *arg)
+{
+ function_called();
+
+ check_expected_ptr(thread);
+ check_expected_ptr(worker);
+ check_expected_ptr(arg);
+}
+
+void __wrap_uv_mutex_lock(uv_mutex_t *mutex)
+{
+ (void)mutex;
+}
+
+void __wrap_uv_mutex_unlock(uv_mutex_t *mutex)
+{
+ (void)mutex;
+}
+
+void __wrap_uv_cond_signal(uv_cond_t *cond_var)
+{
+ (void)cond_var;
+}
+
+void __wrap_uv_cond_wait(uv_cond_t *cond_var, uv_mutex_t *mutex)
+{
+ (void)cond_var;
+ (void)mutex;
+}
+
+ssize_t __wrap_recv(int sockfd, void *buf, size_t len, int flags)
+{
+ function_called();
+
+ check_expected(sockfd);
+ check_expected_ptr(buf);
+ check_expected(len);
+ check_expected(flags);
+
+ char *mock_string = "Test recv";
+ strcpy(buf, mock_string);
+
+ return strlen(mock_string);
+}
+
+ssize_t __wrap_send(int sockfd, const void *buf, size_t len, int flags)
+{
+ function_called();
+
+ check_expected(sockfd);
+ check_expected_ptr(buf);
+ check_expected_ptr(buf);
+ check_expected(len);
+ check_expected(flags);
+
+ return strlen(buf);
+}
diff --git a/exporting/tests/test_exporting_engine.c b/exporting/tests/test_exporting_engine.c
new file mode 100644
index 000000000..774d1a265
--- /dev/null
+++ b/exporting/tests/test_exporting_engine.c
@@ -0,0 +1,1939 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "test_exporting_engine.h"
+#include "libnetdata/required_dummies.h"
+
+RRDHOST *localhost;
+netdata_rwlock_t rrd_rwlock;
+
+// global variables needed by read_exporting_config()
+struct config netdata_config;
+char *netdata_configured_user_config_dir = ".";
+char *netdata_configured_stock_config_dir = ".";
+char *netdata_configured_hostname = "test_global_host";
+
+char log_line[MAX_LOG_LINE + 1];
+
+BACKEND_OPTIONS global_backend_options = 0;
+const char *global_backend_source = "average";
+const char *global_backend_prefix = "netdata";
+
+void init_connectors_in_tests(struct engine *engine)
+{
+ expect_function_call(__wrap_now_realtime_sec);
+ will_return(__wrap_now_realtime_sec, 2);
+
+ expect_function_call(__wrap_uv_thread_create);
+
+ expect_value(__wrap_uv_thread_create, thread, &engine->instance_root->thread);
+ expect_value(__wrap_uv_thread_create, worker, simple_connector_worker);
+ expect_value(__wrap_uv_thread_create, arg, engine->instance_root);
+
+ expect_function_call(__wrap_uv_thread_set_name_np);
+
+ assert_int_equal(__real_init_connectors(engine), 0);
+
+ assert_int_equal(engine->now, 2);
+ assert_int_equal(engine->instance_root->after, 2);
+}
+
+static void test_exporting_engine(void **state)
+{
+ struct engine *engine = *state;
+
+ expect_function_call(__wrap_read_exporting_config);
+ will_return(__wrap_read_exporting_config, engine);
+
+ expect_function_call(__wrap_init_connectors);
+ expect_memory(__wrap_init_connectors, engine, engine, sizeof(struct engine));
+ will_return(__wrap_init_connectors, 0);
+
+ expect_function_call(__wrap_create_main_rusage_chart);
+ expect_not_value(__wrap_create_main_rusage_chart, st_rusage, NULL);
+ expect_not_value(__wrap_create_main_rusage_chart, rd_user, NULL);
+ expect_not_value(__wrap_create_main_rusage_chart, rd_system, NULL);
+
+ expect_function_call(__wrap_now_realtime_sec);
+ will_return(__wrap_now_realtime_sec, 2);
+
+ expect_function_call(__wrap_mark_scheduled_instances);
+ expect_memory(__wrap_mark_scheduled_instances, engine, engine, sizeof(struct engine));
+ will_return(__wrap_mark_scheduled_instances, 1);
+
+ expect_function_call(__wrap_prepare_buffers);
+ expect_memory(__wrap_prepare_buffers, engine, engine, sizeof(struct engine));
+ will_return(__wrap_prepare_buffers, 0);
+
+ expect_function_call(__wrap_send_main_rusage);
+ expect_value(__wrap_send_main_rusage, st_rusage, NULL);
+ expect_value(__wrap_send_main_rusage, rd_user, NULL);
+ expect_value(__wrap_send_main_rusage, rd_system, NULL);
+
+ void *ptr = malloc(sizeof(struct netdata_static_thread));
+ assert_ptr_equal(exporting_main(ptr), NULL);
+ assert_int_equal(engine->now, 2);
+ free(ptr);
+}
+
+static void test_read_exporting_config(void **state)
+{
+ struct engine *engine = __mock_read_exporting_config(); // TODO: use real read_exporting_config() function
+ *state = engine;
+
+ assert_ptr_not_equal(engine, NULL);
+ assert_string_equal(engine->config.hostname, "test_engine_host");
+ assert_int_equal(engine->config.update_every, 3);
+ assert_int_equal(engine->instance_num, 0);
+
+
+ struct instance *instance = engine->instance_root;
+ assert_ptr_not_equal(instance, NULL);
+ assert_ptr_equal(instance->next, NULL);
+ assert_ptr_equal(instance->engine, engine);
+ assert_int_equal(instance->config.type, EXPORTING_CONNECTOR_TYPE_GRAPHITE);
+ assert_string_equal(instance->config.destination, "localhost");
+ assert_string_equal(instance->config.prefix, "netdata");
+ assert_int_equal(instance->config.update_every, 1);
+ assert_int_equal(instance->config.buffer_on_failures, 10);
+ assert_int_equal(instance->config.timeoutms, 10000);
+ assert_true(simple_pattern_matches(instance->config.charts_pattern, "any_chart"));
+ assert_true(simple_pattern_matches(instance->config.hosts_pattern, "anyt_host"));
+ assert_int_equal(instance->config.options, EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES);
+
+ teardown_configured_engine(state);
+}
+
+static void test_init_connectors(void **state)
+{
+ struct engine *engine = *state;
+
+ init_connectors_in_tests(engine);
+
+ assert_int_equal(engine->instance_num, 1);
+
+ struct instance *instance = engine->instance_root;
+
+ assert_ptr_equal(instance->next, NULL);
+ assert_int_equal(instance->index, 0);
+
+ struct simple_connector_config *connector_specific_config = instance->config.connector_specific_config;
+ assert_int_equal(connector_specific_config->default_port, 2003);
+
+ assert_ptr_equal(instance->worker, simple_connector_worker);
+ assert_ptr_equal(instance->start_batch_formatting, NULL);
+ assert_ptr_equal(instance->start_host_formatting, format_host_labels_graphite_plaintext);
+ assert_ptr_equal(instance->start_chart_formatting, NULL);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_graphite_plaintext);
+ assert_ptr_equal(instance->end_chart_formatting, NULL);
+ assert_ptr_equal(instance->end_host_formatting, flush_host_labels);
+
+ BUFFER *buffer = instance->buffer;
+ assert_ptr_not_equal(buffer, NULL);
+ buffer_sprintf(buffer, "%s", "graphite test");
+ assert_string_equal(buffer_tostring(buffer), "graphite test");
+}
+
+static void test_init_graphite_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_graphite_instance(instance), 0);
+ assert_int_equal(
+ ((struct simple_connector_config *)(instance->config.connector_specific_config))->default_port, 2003);
+ freez(instance->config.connector_specific_config);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_graphite_plaintext);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_graphite_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_graphite_plaintext);
+}
+
+static void test_init_json_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_json_instance(instance), 0);
+ assert_int_equal(
+ ((struct simple_connector_config *)(instance->config.connector_specific_config))->default_port, 5448);
+ freez(instance->config.connector_specific_config);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_json_plaintext);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_json_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_json_plaintext);
+}
+
+static void test_init_opentsdb_telnet_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_opentsdb_telnet_instance(instance), 0);
+ assert_int_equal(
+ ((struct simple_connector_config *)(instance->config.connector_specific_config))->default_port, 4242);
+ freez(instance->config.connector_specific_config);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_opentsdb_telnet);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_opentsdb_telnet_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_opentsdb_telnet);
+}
+
+static void test_init_opentsdb_http_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_opentsdb_http_instance(instance), 0);
+ assert_int_equal(
+ ((struct simple_connector_config *)(instance->config.connector_specific_config))->default_port, 4242);
+ freez(instance->config.connector_specific_config);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_opentsdb_http);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+ assert_int_equal(init_opentsdb_http_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_opentsdb_http);
+}
+
+static void test_mark_scheduled_instances(void **state)
+{
+ struct engine *engine = *state;
+
+ assert_int_equal(__real_mark_scheduled_instances(engine), 1);
+
+ struct instance *instance = engine->instance_root;
+ assert_int_equal(instance->scheduled, 1);
+ assert_int_equal(instance->before, 2);
+}
+
+static void test_rrdhost_is_exportable(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ expect_function_call(__wrap_info_int);
+
+ assert_ptr_equal(localhost->exporting_flags, NULL);
+
+ assert_int_equal(__real_rrdhost_is_exportable(instance, localhost), 1);
+
+ assert_string_equal(log_line, "enabled exporting of host 'localhost' for instance 'instance_name'");
+
+ assert_ptr_not_equal(localhost->exporting_flags, NULL);
+ assert_int_equal(localhost->exporting_flags[0], RRDHOST_FLAG_BACKEND_SEND);
+}
+
+static void test_false_rrdhost_is_exportable(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ simple_pattern_free(instance->config.hosts_pattern);
+ instance->config.hosts_pattern = simple_pattern_create("!*", NULL, SIMPLE_PATTERN_EXACT);
+
+ expect_function_call(__wrap_info_int);
+
+ assert_ptr_equal(localhost->exporting_flags, NULL);
+
+ assert_int_equal(__real_rrdhost_is_exportable(instance, localhost), 0);
+
+ assert_string_equal(log_line, "disabled exporting of host 'localhost' for instance 'instance_name'");
+
+ assert_ptr_not_equal(localhost->exporting_flags, NULL);
+ assert_int_equal(localhost->exporting_flags[0], RRDHOST_FLAG_BACKEND_DONT_SEND);
+}
+
+static void test_rrdset_is_exportable(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ RRDSET *st = localhost->rrdset_root;
+
+ assert_ptr_equal(st->exporting_flags, NULL);
+
+ assert_int_equal(__real_rrdset_is_exportable(instance, st), 1);
+
+ assert_ptr_not_equal(st->exporting_flags, NULL);
+ assert_int_equal(st->exporting_flags[0], RRDSET_FLAG_BACKEND_SEND);
+}
+
+static void test_false_rrdset_is_exportable(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ RRDSET *st = localhost->rrdset_root;
+
+ simple_pattern_free(instance->config.charts_pattern);
+ instance->config.charts_pattern = simple_pattern_create("!*", NULL, SIMPLE_PATTERN_EXACT);
+
+ assert_ptr_equal(st->exporting_flags, NULL);
+
+ assert_int_equal(__real_rrdset_is_exportable(instance, st), 0);
+
+ assert_ptr_not_equal(st->exporting_flags, NULL);
+ assert_int_equal(st->exporting_flags[0], RRDSET_FLAG_BACKEND_IGNORE);
+}
+
+static void test_exporting_calculate_value_from_stored_data(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ time_t timestamp;
+
+ instance->after = 3;
+ instance->before = 10;
+
+ expect_function_call(__mock_rrddim_query_oldest_time);
+ will_return(__mock_rrddim_query_oldest_time, 1);
+
+ expect_function_call(__mock_rrddim_query_latest_time);
+ will_return(__mock_rrddim_query_latest_time, 2);
+
+ expect_function_call(__mock_rrddim_query_init);
+ expect_value(__mock_rrddim_query_init, start_time, 1);
+ expect_value(__mock_rrddim_query_init, end_time, 2);
+
+ expect_function_call(__mock_rrddim_query_is_finished);
+ will_return(__mock_rrddim_query_is_finished, 0);
+ expect_function_call(__mock_rrddim_query_next_metric);
+ will_return(__mock_rrddim_query_next_metric, pack_storage_number(27, SN_EXISTS));
+
+ expect_function_call(__mock_rrddim_query_is_finished);
+ will_return(__mock_rrddim_query_is_finished, 0);
+ expect_function_call(__mock_rrddim_query_next_metric);
+ will_return(__mock_rrddim_query_next_metric, pack_storage_number(45, SN_EXISTS));
+
+ expect_function_call(__mock_rrddim_query_is_finished);
+ will_return(__mock_rrddim_query_is_finished, 1);
+
+ expect_function_call(__mock_rrddim_query_finalize);
+
+ assert_int_equal(__real_exporting_calculate_value_from_stored_data(instance, rd, &timestamp), 36);
+}
+
+static void test_prepare_buffers(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->start_batch_formatting = __mock_start_batch_formatting;
+ instance->start_host_formatting = __mock_start_host_formatting;
+ instance->start_chart_formatting = __mock_start_chart_formatting;
+ instance->metric_formatting = __mock_metric_formatting;
+ instance->end_chart_formatting = __mock_end_chart_formatting;
+ instance->end_host_formatting = __mock_end_host_formatting;
+ instance->end_batch_formatting = __mock_end_batch_formatting;
+ __real_mark_scheduled_instances(engine);
+
+ expect_function_call(__mock_start_batch_formatting);
+ expect_value(__mock_start_batch_formatting, instance, instance);
+ will_return(__mock_start_batch_formatting, 0);
+
+ expect_function_call(__wrap_rrdhost_is_exportable);
+ expect_value(__wrap_rrdhost_is_exportable, instance, instance);
+ expect_value(__wrap_rrdhost_is_exportable, host, localhost);
+ will_return(__wrap_rrdhost_is_exportable, 1);
+
+ expect_function_call(__mock_start_host_formatting);
+ expect_value(__mock_start_host_formatting, instance, instance);
+ expect_value(__mock_start_host_formatting, host, localhost);
+ will_return(__mock_start_host_formatting, 0);
+
+ RRDSET *st = localhost->rrdset_root;
+ expect_function_call(__wrap_rrdset_is_exportable);
+ expect_value(__wrap_rrdset_is_exportable, instance, instance);
+ expect_value(__wrap_rrdset_is_exportable, st, st);
+ will_return(__wrap_rrdset_is_exportable, 1);
+
+ expect_function_call(__mock_start_chart_formatting);
+ expect_value(__mock_start_chart_formatting, instance, instance);
+ expect_value(__mock_start_chart_formatting, st, st);
+ will_return(__mock_start_chart_formatting, 0);
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ expect_function_call(__mock_metric_formatting);
+ expect_value(__mock_metric_formatting, instance, instance);
+ expect_value(__mock_metric_formatting, rd, rd);
+ will_return(__mock_metric_formatting, 0);
+
+ expect_function_call(__mock_end_chart_formatting);
+ expect_value(__mock_end_chart_formatting, instance, instance);
+ expect_value(__mock_end_chart_formatting, st, st);
+ will_return(__mock_end_chart_formatting, 0);
+
+ expect_function_call(__mock_end_host_formatting);
+ expect_value(__mock_end_host_formatting, instance, instance);
+ expect_value(__mock_end_host_formatting, host, localhost);
+ will_return(__mock_end_host_formatting, 0);
+
+ expect_function_call(__mock_end_batch_formatting);
+ expect_value(__mock_end_batch_formatting, instance, instance);
+ will_return(__mock_end_batch_formatting, 0);
+
+ assert_int_equal(__real_prepare_buffers(engine), 0);
+
+ assert_int_equal(instance->stats.buffered_metrics, 1);
+
+ // check with NULL functions
+ instance->start_batch_formatting = NULL;
+ instance->start_host_formatting = NULL;
+ instance->start_chart_formatting = NULL;
+ instance->metric_formatting = NULL;
+ instance->end_chart_formatting = NULL;
+ instance->end_host_formatting = NULL;
+ instance->end_batch_formatting = NULL;
+ assert_int_equal(__real_prepare_buffers(engine), 0);
+
+ assert_int_equal(instance->scheduled, 0);
+ assert_int_equal(instance->after, 2);
+}
+
+static void test_exporting_name_copy(void **state)
+{
+ (void)state;
+
+ char *source_name = "test.name-with/special#characters_";
+ char destination_name[RRD_ID_LENGTH_MAX + 1];
+
+ assert_int_equal(exporting_name_copy(destination_name, source_name, RRD_ID_LENGTH_MAX), 34);
+ assert_string_equal(destination_name, "test.name_with_special_characters_");
+}
+
+static void test_format_dimension_collected_graphite_plaintext(void **state)
+{
+ struct engine *engine = *state;
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_collected_graphite_plaintext(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "netdata.test-host.chart_name.dimension_name;TAG1=VALUE1 TAG2=VALUE2 123000321 15051\n");
+}
+
+static void test_format_dimension_stored_graphite_plaintext(void **state)
+{
+ struct engine *engine = *state;
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_stored_graphite_plaintext(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "netdata.test-host.chart_name.dimension_name;TAG1=VALUE1 TAG2=VALUE2 690565856.0000000 15052\n");
+}
+
+static void test_format_dimension_collected_json_plaintext(void **state)
+{
+ struct engine *engine = *state;
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_collected_json_plaintext(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "{\"prefix\":\"netdata\",\"hostname\":\"test-host\",\"host_tags\":\"TAG1=VALUE1 TAG2=VALUE2\","
+ "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"(null)\","
+ "\"chart_context\":\"(null)\",\"chart_type\":\"(null)\",\"units\":\"(null)\",\"id\":\"dimension_id\","
+ "\"name\":\"dimension_name\",\"value\":123000321,\"timestamp\":15051}\n");
+}
+
+static void test_format_dimension_stored_json_plaintext(void **state)
+{
+ struct engine *engine = *state;
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_stored_json_plaintext(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "{\"prefix\":\"netdata\",\"hostname\":\"test-host\",\"host_tags\":\"TAG1=VALUE1 TAG2=VALUE2\","
+ "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"(null)\"," \
+ "\"chart_context\": \"(null)\",\"chart_type\":\"(null)\",\"units\": \"(null)\",\"id\":\"dimension_id\","
+ "\"name\":\"dimension_name\",\"value\":690565856.0000000,\"timestamp\": 15052}\n");
+}
+
+static void test_format_dimension_collected_opentsdb_telnet(void **state)
+{
+ struct engine *engine = *state;
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_collected_opentsdb_telnet(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "put netdata.chart_name.dimension_name 15051 123000321 host=test-host TAG1=VALUE1 TAG2=VALUE2\n");
+}
+
+static void test_format_dimension_stored_opentsdb_telnet(void **state)
+{
+ struct engine *engine = *state;
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_stored_opentsdb_telnet(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "put netdata.chart_name.dimension_name 15052 690565856.0000000 host=test-host TAG1=VALUE1 TAG2=VALUE2\n");
+}
+
+static void test_format_dimension_collected_opentsdb_http(void **state)
+{
+ struct engine *engine = *state;
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_collected_opentsdb_http(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "{\"metric\":\"netdata.chart_name.dimension_name\","
+ "\"timestamp\":15051,"
+ "\"value\":123000321,"
+ "\"tags\":{\"host\":\"test-host TAG1=VALUE1 TAG2=VALUE2\"}}");
+}
+
+static void test_format_dimension_stored_opentsdb_http(void **state)
+{
+ struct engine *engine = *state;
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+ assert_int_equal(format_dimension_stored_opentsdb_http(engine->instance_root, rd), 0);
+ assert_string_equal(
+ buffer_tostring(engine->instance_root->buffer),
+ "{\"metric\":\"netdata.chart_name.dimension_name\","
+ "\"timestamp\":15052,"
+ "\"value\":690565856.0000000,"
+ "\"tags\":{\"host\":\"test-host TAG1=VALUE1 TAG2=VALUE2\"}}");
+}
+
+static void test_exporting_discard_response(void **state)
+{
+ struct engine *engine = *state;
+
+ BUFFER *response = buffer_create(0);
+ buffer_sprintf(response, "Test response");
+
+ assert_int_equal(exporting_discard_response(response, engine->instance_root), 0);
+ assert_int_equal(buffer_strlen(response), 0);
+
+ buffer_free(response);
+}
+
+static void test_simple_connector_receive_response(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+
+ int sock = 1;
+
+ expect_function_call(__wrap_recv);
+ expect_value(__wrap_recv, sockfd, 1);
+ expect_not_value(__wrap_recv, buf, 0);
+ expect_value(__wrap_recv, len, 4096);
+ expect_value(__wrap_recv, flags, MSG_DONTWAIT);
+
+ simple_connector_receive_response(&sock, instance);
+
+ assert_int_equal(stats->received_bytes, 9);
+ assert_int_equal(stats->receptions, 1);
+ assert_int_equal(sock, 1);
+}
+
+static void test_simple_connector_send_buffer(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+
+ int sock = 1;
+ int failures = 3;
+ size_t buffered_metrics = 1;
+ BUFFER *header = buffer_create(0);
+ BUFFER *buffer = buffer_create(0);
+ buffer_strcat(header, "test header\n");
+ buffer_strcat(buffer, "test buffer\n");
+
+ expect_function_call(__wrap_send);
+ expect_value(__wrap_send, sockfd, 1);
+ expect_value(__wrap_send, buf, buffer_tostring(header));
+ expect_string(__wrap_send, buf, "test header\n");
+ expect_value(__wrap_send, len, 12);
+ expect_value(__wrap_send, flags, MSG_NOSIGNAL);
+
+ expect_function_call(__wrap_send);
+ expect_value(__wrap_send, sockfd, 1);
+ expect_value(__wrap_send, buf, buffer_tostring(buffer));
+ expect_string(__wrap_send, buf, "test buffer\n");
+ expect_value(__wrap_send, len, 12);
+ expect_value(__wrap_send, flags, MSG_NOSIGNAL);
+
+ simple_connector_send_buffer(&sock, &failures, instance, header, buffer, buffered_metrics);
+
+ assert_int_equal(failures, 0);
+ assert_int_equal(stats->transmission_successes, 1);
+ assert_int_equal(stats->sent_bytes, 12);
+ assert_int_equal(stats->sent_metrics, 1);
+ assert_int_equal(stats->transmission_failures, 0);
+
+ assert_int_equal(buffer_strlen(buffer), 0);
+
+ assert_int_equal(sock, 1);
+}
+
+static void test_simple_connector_worker(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+
+ __real_mark_scheduled_instances(engine);
+
+ struct simple_connector_data *simple_connector_data = callocz(1, sizeof(struct simple_connector_data));
+ instance->connector_specific_data = simple_connector_data;
+ simple_connector_data->last_buffer = callocz(1, sizeof(struct simple_connector_buffer));
+ simple_connector_data->first_buffer = simple_connector_data->last_buffer;
+ simple_connector_data->header = buffer_create(0);
+ simple_connector_data->buffer = buffer_create(0);
+ simple_connector_data->last_buffer->header = buffer_create(0);
+ simple_connector_data->last_buffer->buffer = buffer_create(0);
+
+ buffer_sprintf(simple_connector_data->last_buffer->header, "test header");
+ buffer_sprintf(simple_connector_data->last_buffer->buffer, "test buffer");
+
+ expect_function_call(__wrap_connect_to_one_of);
+ expect_string(__wrap_connect_to_one_of, destination, "localhost");
+ expect_value(__wrap_connect_to_one_of, default_port, 2003);
+ expect_not_value(__wrap_connect_to_one_of, reconnects_counter, 0);
+ expect_value(__wrap_connect_to_one_of, connected_to, 0);
+ expect_value(__wrap_connect_to_one_of, connected_to_size, 0);
+ will_return(__wrap_connect_to_one_of, 2);
+
+ expect_function_call(__wrap_send);
+ expect_value(__wrap_send, sockfd, 2);
+ expect_not_value(__wrap_send, buf, buffer_tostring(simple_connector_data->last_buffer->buffer));
+ expect_string(__wrap_send, buf, "test header");
+ expect_value(__wrap_send, len, 11);
+ expect_value(__wrap_send, flags, MSG_NOSIGNAL);
+
+ expect_function_call(__wrap_send);
+ expect_value(__wrap_send, sockfd, 2);
+ expect_value(__wrap_send, buf, buffer_tostring(simple_connector_data->last_buffer->buffer));
+ expect_string(__wrap_send, buf, "test buffer");
+ expect_value(__wrap_send, len, 11);
+ expect_value(__wrap_send, flags, MSG_NOSIGNAL);
+
+ expect_function_call(__wrap_send_internal_metrics);
+ expect_value(__wrap_send_internal_metrics, instance, instance);
+ will_return(__wrap_send_internal_metrics, 0);
+
+ simple_connector_worker(instance);
+
+ assert_int_equal(stats->buffered_metrics, 0);
+ assert_int_equal(stats->buffered_bytes, 0);
+ assert_int_equal(stats->received_bytes, 0);
+ assert_int_equal(stats->sent_bytes, 0);
+ assert_int_equal(stats->sent_metrics, 0);
+ assert_int_equal(stats->lost_metrics, 0);
+ assert_int_equal(stats->receptions, 0);
+ assert_int_equal(stats->transmission_successes, 0);
+ assert_int_equal(stats->transmission_failures, 0);
+ assert_int_equal(stats->data_lost_events, 0);
+ assert_int_equal(stats->lost_bytes, 0);
+ assert_int_equal(stats->reconnects, 0);
+}
+
+static void test_sanitize_json_string(void **state)
+{
+ (void)state;
+
+ char *src = "check \t\\\" string";
+ char dst[19 + 1];
+
+ sanitize_json_string(dst, src, 19);
+
+ assert_string_equal(dst, "check _\\\\\\\" string");
+}
+
+static void test_sanitize_graphite_label_value(void **state)
+{
+ (void)state;
+
+ char *src = "check ;~ string";
+ char dst[15 + 1];
+
+ sanitize_graphite_label_value(dst, src, 15);
+
+ assert_string_equal(dst, "check____string");
+}
+
+static void test_sanitize_opentsdb_label_value(void **state)
+{
+ (void)state;
+
+ char *src = "check \t\\\" #&$? -_./ string";
+ char dst[26 + 1];
+
+ sanitize_opentsdb_label_value(dst, src, 26);
+
+ assert_string_equal(dst, "check__________-_./_string");
+}
+
+static void test_format_host_labels_json_plaintext(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ assert_int_equal(format_host_labels_json_plaintext(instance, localhost), 0);
+ assert_string_equal(buffer_tostring(instance->labels), "\"labels\":{\"key1\":\"value1\",\"key2\":\"value2\"},");
+}
+
+static void test_format_host_labels_graphite_plaintext(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ assert_int_equal(format_host_labels_graphite_plaintext(instance, localhost), 0);
+ assert_string_equal(buffer_tostring(instance->labels), ";key1=value1;key2=value2");
+}
+
+static void test_format_host_labels_opentsdb_telnet(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ assert_int_equal(format_host_labels_opentsdb_telnet(instance, localhost), 0);
+ assert_string_equal(buffer_tostring(instance->labels), " key1=value1 key2=value2");
+}
+
+static void test_format_host_labels_opentsdb_http(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ assert_int_equal(format_host_labels_opentsdb_http(instance, localhost), 0);
+ assert_string_equal(buffer_tostring(instance->labels), ",\"key1\":\"value1\",\"key2\":\"value2\"");
+}
+
+static void test_flush_host_labels(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->labels = buffer_create(12);
+ buffer_strcat(instance->labels, "check string");
+ assert_int_equal(buffer_strlen(instance->labels), 12);
+
+ assert_int_equal(flush_host_labels(instance, localhost), 0);
+ assert_int_equal(buffer_strlen(instance->labels), 0);
+}
+
+static void test_create_main_rusage_chart(void **state)
+{
+ UNUSED(state);
+
+ RRDSET *st_rusage = calloc(1, sizeof(RRDSET));
+ RRDDIM *rd_user = NULL;
+ RRDDIM *rd_system = NULL;
+
+ expect_function_call(rrdset_create_custom);
+ expect_value(rrdset_create_custom, host, localhost);
+ expect_string(rrdset_create_custom, type, "netdata");
+ expect_string(rrdset_create_custom, id, "exporting_main_thread_cpu");
+ expect_value(rrdset_create_custom, name, NULL);
+ expect_string(rrdset_create_custom, family, "exporting");
+ expect_string(rrdset_create_custom, context, "exporting_cpu_usage");
+ expect_string(rrdset_create_custom, units, "milliseconds/s");
+ expect_string(rrdset_create_custom, plugin, "exporting");
+ expect_value(rrdset_create_custom, module, NULL);
+ expect_value(rrdset_create_custom, priority, 130600);
+ expect_value(rrdset_create_custom, update_every, localhost->rrd_update_every);
+ expect_value(rrdset_create_custom, chart_type, RRDSET_TYPE_STACKED);
+ will_return(rrdset_create_custom, st_rusage);
+
+ expect_function_calls(rrddim_add_custom, 2);
+ expect_value_count(rrddim_add_custom, st, st_rusage, 2);
+ expect_value_count(rrddim_add_custom, name, NULL, 2);
+ expect_value_count(rrddim_add_custom, multiplier, 1, 2);
+ expect_value_count(rrddim_add_custom, divisor, 1000, 2);
+ expect_value_count(rrddim_add_custom, algorithm, RRD_ALGORITHM_INCREMENTAL, 2);
+
+ __real_create_main_rusage_chart(&st_rusage, &rd_user, &rd_system);
+
+ free(st_rusage);
+}
+
+static void test_send_main_rusage(void **state)
+{
+ UNUSED(state);
+
+ RRDSET *st_rusage = calloc(1, sizeof(RRDSET));
+ st_rusage->counter_done = 1;
+
+ expect_function_call(rrdset_next_usec);
+ expect_value(rrdset_next_usec, st, st_rusage);
+
+ expect_function_calls(rrddim_set_by_pointer, 2);
+ expect_value_count(rrddim_set_by_pointer, st, st_rusage, 2);
+
+ expect_function_call(rrdset_done);
+ expect_value(rrdset_done, st, st_rusage);
+
+ __real_send_main_rusage(st_rusage, NULL, NULL);
+
+ free(st_rusage);
+}
+
+static void test_send_internal_metrics(void **state)
+{
+ UNUSED(state);
+
+ struct instance *instance = calloc(1, sizeof(struct instance));
+ instance->config.name = (const char *)strdupz("test_instance");
+ instance->config.update_every = 2;
+
+ struct stats *stats = &instance->stats;
+
+ stats->st_metrics = calloc(1, sizeof(RRDSET));
+ stats->st_metrics->counter_done = 1;
+ stats->st_bytes = calloc(1, sizeof(RRDSET));
+ stats->st_bytes->counter_done = 1;
+ stats->st_ops = calloc(1, sizeof(RRDSET));
+ stats->st_ops->counter_done = 1;
+ stats->st_rusage = calloc(1, sizeof(RRDSET));
+ stats->st_rusage->counter_done = 1;
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_create_custom);
+ expect_value(rrdset_create_custom, host, localhost);
+ expect_string(rrdset_create_custom, type, "netdata");
+ expect_string(rrdset_create_custom, id, "exporting_test_instance_metrics");
+ expect_value(rrdset_create_custom, name, NULL);
+ expect_string(rrdset_create_custom, family, "exporting_test_instance");
+ expect_string(rrdset_create_custom, context, "exporting_buffer");
+ expect_string(rrdset_create_custom, units, "metrics");
+ expect_string(rrdset_create_custom, plugin, "exporting");
+ expect_value(rrdset_create_custom, module, NULL);
+ expect_value(rrdset_create_custom, priority, 130610);
+ expect_value(rrdset_create_custom, update_every, 2);
+ expect_value(rrdset_create_custom, chart_type, RRDSET_TYPE_LINE);
+ will_return(rrdset_create_custom, stats->st_metrics);
+
+ expect_function_calls(rrddim_add_custom, 3);
+ expect_value_count(rrddim_add_custom, st, stats->st_metrics, 3);
+ expect_value_count(rrddim_add_custom, name, NULL, 3);
+ expect_value_count(rrddim_add_custom, multiplier, 1, 3);
+ expect_value_count(rrddim_add_custom, divisor, 1, 3);
+ expect_value_count(rrddim_add_custom, algorithm, RRD_ALGORITHM_ABSOLUTE, 3);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_create_custom);
+ expect_value(rrdset_create_custom, host, localhost);
+ expect_string(rrdset_create_custom, type, "netdata");
+ expect_string(rrdset_create_custom, id, "exporting_test_instance_bytes");
+ expect_value(rrdset_create_custom, name, NULL);
+ expect_string(rrdset_create_custom, family, "exporting_test_instance");
+ expect_string(rrdset_create_custom, context, "exporting_data_size");
+ expect_string(rrdset_create_custom, units, "KiB");
+ expect_string(rrdset_create_custom, plugin, "exporting");
+ expect_value(rrdset_create_custom, module, NULL);
+ expect_value(rrdset_create_custom, priority, 130620);
+ expect_value(rrdset_create_custom, update_every, 2);
+ expect_value(rrdset_create_custom, chart_type, RRDSET_TYPE_AREA);
+ will_return(rrdset_create_custom, stats->st_bytes);
+
+ expect_function_calls(rrddim_add_custom, 4);
+ expect_value_count(rrddim_add_custom, st, stats->st_bytes, 4);
+ expect_value_count(rrddim_add_custom, name, NULL, 4);
+ expect_value_count(rrddim_add_custom, multiplier, 1, 4);
+ expect_value_count(rrddim_add_custom, divisor, 1024, 4);
+ expect_value_count(rrddim_add_custom, algorithm, RRD_ALGORITHM_ABSOLUTE, 4);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_create_custom);
+ expect_value(rrdset_create_custom, host, localhost);
+ expect_string(rrdset_create_custom, type, "netdata");
+ expect_string(rrdset_create_custom, id, "exporting_test_instance_ops");
+ expect_value(rrdset_create_custom, name, NULL);
+ expect_string(rrdset_create_custom, family, "exporting_test_instance");
+ expect_string(rrdset_create_custom, context, "exporting_operations");
+ expect_string(rrdset_create_custom, units, "operations");
+ expect_string(rrdset_create_custom, plugin, "exporting");
+ expect_value(rrdset_create_custom, module, NULL);
+ expect_value(rrdset_create_custom, priority, 130630);
+ expect_value(rrdset_create_custom, update_every, 2);
+ expect_value(rrdset_create_custom, chart_type, RRDSET_TYPE_LINE);
+ will_return(rrdset_create_custom, stats->st_ops);
+
+ expect_function_calls(rrddim_add_custom, 5);
+ expect_value_count(rrddim_add_custom, st, stats->st_ops, 5);
+ expect_value_count(rrddim_add_custom, name, NULL, 5);
+ expect_value_count(rrddim_add_custom, multiplier, 1, 5);
+ expect_value_count(rrddim_add_custom, divisor, 1, 5);
+ expect_value_count(rrddim_add_custom, algorithm, RRD_ALGORITHM_ABSOLUTE, 5);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_create_custom);
+ expect_value(rrdset_create_custom, host, localhost);
+ expect_string(rrdset_create_custom, type, "netdata");
+ expect_string(rrdset_create_custom, id, "exporting_test_instance_thread_cpu");
+ expect_value(rrdset_create_custom, name, NULL);
+ expect_string(rrdset_create_custom, family, "exporting_test_instance");
+ expect_string(rrdset_create_custom, context, "exporting_instance");
+ expect_string(rrdset_create_custom, units, "milliseconds/s");
+ expect_string(rrdset_create_custom, plugin, "exporting");
+ expect_value(rrdset_create_custom, module, NULL);
+ expect_value(rrdset_create_custom, priority, 130640);
+ expect_value(rrdset_create_custom, update_every, 2);
+ expect_value(rrdset_create_custom, chart_type, RRDSET_TYPE_STACKED);
+ will_return(rrdset_create_custom, stats->st_rusage);
+
+ expect_function_calls(rrddim_add_custom, 2);
+ expect_value_count(rrddim_add_custom, st, stats->st_rusage, 2);
+ expect_value_count(rrddim_add_custom, name, NULL, 2);
+ expect_value_count(rrddim_add_custom, multiplier, 1, 2);
+ expect_value_count(rrddim_add_custom, divisor, 1000, 2);
+ expect_value_count(rrddim_add_custom, algorithm, RRD_ALGORITHM_INCREMENTAL, 2);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_next_usec);
+ expect_value(rrdset_next_usec, st, stats->st_metrics);
+
+ expect_function_calls(rrddim_set_by_pointer, 3);
+ expect_value_count(rrddim_set_by_pointer, st, stats->st_metrics, 3);
+
+ expect_function_call(rrdset_done);
+ expect_value(rrdset_done, st, stats->st_metrics);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_next_usec);
+ expect_value(rrdset_next_usec, st, stats->st_bytes);
+
+ expect_function_calls(rrddim_set_by_pointer, 4);
+ expect_value_count(rrddim_set_by_pointer, st, stats->st_bytes, 4);
+
+ expect_function_call(rrdset_done);
+ expect_value(rrdset_done, st, stats->st_bytes);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_next_usec);
+ expect_value(rrdset_next_usec, st, stats->st_ops);
+
+ expect_function_calls(rrddim_set_by_pointer, 5);
+ expect_value_count(rrddim_set_by_pointer, st, stats->st_ops, 5);
+
+ expect_function_call(rrdset_done);
+ expect_value(rrdset_done, st, stats->st_ops);
+
+ // ------------------------------------------------------------------------
+
+ expect_function_call(rrdset_next_usec);
+ expect_value(rrdset_next_usec, st, stats->st_rusage);
+
+ expect_function_calls(rrddim_set_by_pointer, 2);
+ expect_value_count(rrddim_set_by_pointer, st, stats->st_rusage, 2);
+
+ expect_function_call(rrdset_done);
+ expect_value(rrdset_done, st, stats->st_rusage);
+
+ // ------------------------------------------------------------------------
+
+ __real_send_internal_metrics(instance);
+
+ free(stats->st_metrics);
+ free(stats->st_bytes);
+ free(stats->st_ops);
+ free(stats->st_rusage);
+ free((void *)instance->config.name);
+ free(instance);
+}
+
+static void test_can_send_rrdset(void **state)
+{
+ (void)*state;
+
+ assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root), 1);
+
+ rrdset_flag_set(localhost->rrdset_root, RRDSET_FLAG_BACKEND_IGNORE);
+ assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root), 0);
+ rrdset_flag_clear(localhost->rrdset_root, RRDSET_FLAG_BACKEND_IGNORE);
+
+ // TODO: test with a denying simple pattern
+
+ rrdset_flag_set(localhost->rrdset_root, RRDSET_FLAG_OBSOLETE);
+ assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root), 0);
+ rrdset_flag_clear(localhost->rrdset_root, RRDSET_FLAG_OBSOLETE);
+
+ localhost->rrdset_root->rrd_memory_mode = RRD_MEMORY_MODE_NONE;
+ prometheus_exporter_instance->config.options |= EXPORTING_SOURCE_DATA_AVERAGE;
+ assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root), 0);
+}
+
+static void test_prometheus_name_copy(void **state)
+{
+ (void)*state;
+
+ char destination_name[PROMETHEUS_ELEMENT_MAX + 1];
+ assert_int_equal(prometheus_name_copy(destination_name, "test-name", PROMETHEUS_ELEMENT_MAX), 9);
+
+ assert_string_equal(destination_name, "test_name");
+}
+
+static void test_prometheus_label_copy(void **state)
+{
+ (void)*state;
+
+ char destination_name[PROMETHEUS_ELEMENT_MAX + 1];
+ assert_int_equal(prometheus_label_copy(destination_name, "test\"\\\nlabel", PROMETHEUS_ELEMENT_MAX), 15);
+
+ assert_string_equal(destination_name, "test\\\"\\\\\\\nlabel");
+}
+
+static void test_prometheus_units_copy(void **state)
+{
+ (void)*state;
+
+ char destination_name[PROMETHEUS_ELEMENT_MAX + 1];
+ assert_string_equal(prometheus_units_copy(destination_name, "test-units", PROMETHEUS_ELEMENT_MAX, 0), "_test_units");
+ assert_string_equal(destination_name, "_test_units");
+
+ assert_string_equal(prometheus_units_copy(destination_name, "%", PROMETHEUS_ELEMENT_MAX, 0), "_percent");
+ assert_string_equal(prometheus_units_copy(destination_name, "test-units/s", PROMETHEUS_ELEMENT_MAX, 0), "_test_units_persec");
+
+ assert_string_equal(prometheus_units_copy(destination_name, "KiB", PROMETHEUS_ELEMENT_MAX, 1), "_KB");
+}
+
+static void test_format_host_labels_prometheus(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ format_host_labels_prometheus(instance, localhost);
+ assert_string_equal(buffer_tostring(instance->labels), "key1=\"netdata\",key2=\"value2\"");
+}
+
+static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state)
+{
+ (void)state;
+
+ BUFFER *buffer = buffer_create(0);
+
+ localhost->hostname = strdupz("test_hostname");
+ localhost->rrdset_root->family = strdupz("test_family");
+ localhost->rrdset_root->context = strdupz("test_context");
+
+ expect_function_call(__wrap_now_realtime_sec);
+ will_return(__wrap_now_realtime_sec, 2);
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(localhost, buffer, "test_server", "test_prefix", 0, 0);
+
+ assert_string_equal(
+ buffer_tostring(buffer),
+ "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\"} 1\n"
+ "netdata_host_tags_info{key1=\"value1\",key2=\"value2\"} 1\n"
+ "netdata_host_tags{key1=\"value1\",key2=\"value2\"} 1\n"
+ "test_prefix_test_context{chart=\"chart_id\",family=\"test_family\",dimension=\"dimension_id\"} 690565856.0000000\n");
+
+ buffer_flush(buffer);
+
+ expect_function_call(__wrap_now_realtime_sec);
+ will_return(__wrap_now_realtime_sec, 2);
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(
+ localhost, buffer, "test_server", "test_prefix", 0, PROMETHEUS_OUTPUT_NAMES | PROMETHEUS_OUTPUT_TYPES);
+
+ assert_string_equal(
+ buffer_tostring(buffer),
+ "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\"} 1\n"
+ "netdata_host_tags_info{key1=\"value1\",key2=\"value2\"} 1\n"
+ "netdata_host_tags{key1=\"value1\",key2=\"value2\"} 1\n"
+ "# TYPE test_prefix_test_context gauge\n"
+ "test_prefix_test_context{chart=\"chart_name\",family=\"test_family\",dimension=\"dimension_name\"} 690565856.0000000\n");
+
+ buffer_flush(buffer);
+
+ expect_function_call(__wrap_now_realtime_sec);
+ will_return(__wrap_now_realtime_sec, 2);
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(localhost, buffer, "test_server", "test_prefix", 0, 0);
+
+ assert_string_equal(
+ buffer_tostring(buffer),
+ "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\"} 1\n"
+ "netdata_host_tags_info{instance=\"test_hostname\",key1=\"value1\",key2=\"value2\"} 1\n"
+ "netdata_host_tags{instance=\"test_hostname\",key1=\"value1\",key2=\"value2\"} 1\n"
+ "test_prefix_test_context{chart=\"chart_id\",family=\"test_family\",dimension=\"dimension_id\",instance=\"test_hostname\"} 690565856.0000000\n");
+
+ free(localhost->rrdset_root->context);
+ free(localhost->rrdset_root->family);
+ free(localhost->hostname);
+ buffer_free(buffer);
+}
+
+#if ENABLE_PROMETHEUS_REMOTE_WRITE
+static void test_init_prometheus_remote_write_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ expect_function_call(__wrap_init_write_request);
+ will_return(__wrap_init_write_request, 0xff);
+
+ assert_int_equal(init_prometheus_remote_write_instance(instance), 0);
+
+ assert_ptr_equal(instance->worker, simple_connector_worker);
+ assert_ptr_equal(instance->start_batch_formatting, NULL);
+ assert_ptr_equal(instance->start_host_formatting, format_host_prometheus_remote_write);
+ assert_ptr_equal(instance->start_chart_formatting, format_chart_prometheus_remote_write);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_prometheus_remote_write);
+ assert_ptr_equal(instance->end_chart_formatting, NULL);
+ assert_ptr_equal(instance->end_host_formatting, NULL);
+ assert_ptr_equal(instance->end_batch_formatting, format_batch_prometheus_remote_write);
+ assert_ptr_equal(instance->prepare_header, prometheus_remote_write_prepare_header);
+ assert_ptr_equal(instance->check_response, process_prometheus_remote_write_response);
+
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ struct prometheus_remote_write_specific_data *connector_specific_data =
+ (struct prometheus_remote_write_specific_data *)instance->connector_specific_data;
+
+ assert_ptr_not_equal(instance->connector_specific_data, NULL);
+ assert_ptr_not_equal(connector_specific_data->write_request, NULL);
+ freez(instance->connector_specific_data);
+}
+
+static void test_prometheus_remote_write_prepare_header(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ struct prometheus_remote_write_specific_config *connector_specific_config =
+ callocz(1, sizeof(struct prometheus_remote_write_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->remote_write_path = strdupz("/receive");
+
+ struct simple_connector_data *simple_connector_data = callocz(1, sizeof(struct simple_connector_data));
+ instance->connector_specific_data = simple_connector_data;
+ simple_connector_data->last_buffer = callocz(1, sizeof(struct simple_connector_buffer));
+ simple_connector_data->last_buffer->header = buffer_create(0);
+ simple_connector_data->last_buffer->buffer = buffer_create(0);
+
+ buffer_sprintf(simple_connector_data->last_buffer->buffer, "test buffer");
+
+ prometheus_remote_write_prepare_header(instance);
+
+ assert_string_equal(
+ buffer_tostring(simple_connector_data->last_buffer->header),
+ "POST /receive HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Accept: */*\r\n"
+ "Content-Encoding: snappy\r\n"
+ "Content-Type: application/x-protobuf\r\n"
+ "X-Prometheus-Remote-Write-Version: 0.1.0\r\n"
+ "Content-Length: 11\r\n"
+ "\r\n");
+
+ free(connector_specific_config->remote_write_path);
+
+ buffer_free(simple_connector_data->last_buffer->header);
+ buffer_free(simple_connector_data->last_buffer->buffer);
+}
+
+static void test_process_prometheus_remote_write_response(void **state)
+{
+ (void)state;
+ BUFFER *buffer = buffer_create(0);
+
+ buffer_sprintf(buffer, "HTTP/1.1 200 OK\r\n");
+ assert_int_equal(process_prometheus_remote_write_response(buffer, NULL), 0);
+
+ buffer_free(buffer);
+}
+
+static void test_format_host_prometheus_remote_write(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
+ instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
+
+ struct simple_connector_data *simple_connector_data = mallocz(sizeof(struct simple_connector_data *));
+ instance->connector_specific_data = simple_connector_data;
+ struct prometheus_remote_write_specific_data *connector_specific_data =
+ mallocz(sizeof(struct prometheus_remote_write_specific_data *));
+ simple_connector_data->connector_specific_data = (void *)connector_specific_data;
+ connector_specific_data->write_request = (void *)0xff;
+
+ localhost->program_name = strdupz("test_program");
+ localhost->program_version = strdupz("test_version");
+
+ expect_function_call(__wrap_add_host_info);
+ expect_value(__wrap_add_host_info, write_request_p, 0xff);
+ expect_string(__wrap_add_host_info, name, "netdata_info");
+ expect_string(__wrap_add_host_info, instance, "test-host");
+ expect_string(__wrap_add_host_info, application, "test_program");
+ expect_string(__wrap_add_host_info, version, "test_version");
+ expect_in_range(
+ __wrap_add_host_info, timestamp, now_realtime_usec() / USEC_PER_MS - 1000, now_realtime_usec() / USEC_PER_MS);
+
+ expect_function_call(__wrap_add_label);
+ expect_value(__wrap_add_label, write_request_p, 0xff);
+ expect_string(__wrap_add_label, key, "key1");
+ expect_string(__wrap_add_label, value, "value1");
+
+ expect_function_call(__wrap_add_label);
+ expect_value(__wrap_add_label, write_request_p, 0xff);
+ expect_string(__wrap_add_label, key, "key2");
+ expect_string(__wrap_add_label, value, "value2");
+
+ assert_int_equal(format_host_prometheus_remote_write(instance, localhost), 0);
+
+ freez(connector_specific_data);
+ freez(simple_connector_data);
+ free(localhost->program_name);
+ free(localhost->program_version);
+}
+
+static void test_format_dimension_prometheus_remote_write(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ struct simple_connector_data *simple_connector_data = mallocz(sizeof(struct simple_connector_data *));
+ instance->connector_specific_data = simple_connector_data;
+ struct prometheus_remote_write_specific_data *connector_specific_data =
+ mallocz(sizeof(struct prometheus_remote_write_specific_data *));
+ simple_connector_data->connector_specific_data = (void *)connector_specific_data;
+ connector_specific_data->write_request = (void *)0xff;
+
+ RRDDIM *rd = localhost->rrdset_root->dimensions;
+
+ expect_function_call(__wrap_exporting_calculate_value_from_stored_data);
+ will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_EXISTS));
+
+ expect_function_call(__wrap_add_metric);
+ expect_value(__wrap_add_metric, write_request_p, 0xff);
+ expect_string(__wrap_add_metric, name, "netdata_");
+ expect_string(__wrap_add_metric, chart, "");
+ expect_string(__wrap_add_metric, family, "");
+ expect_string(__wrap_add_metric, dimension, "dimension_name");
+ expect_string(__wrap_add_metric, instance, "test-host");
+ expect_value(__wrap_add_metric, value, 0x292932e0);
+ expect_value(__wrap_add_metric, timestamp, 15052 * MSEC_PER_SEC);
+
+ assert_int_equal(format_dimension_prometheus_remote_write(instance, rd), 0);
+}
+
+static void test_format_batch_prometheus_remote_write(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ struct simple_connector_data *simple_connector_data = mallocz(sizeof(struct simple_connector_data *));
+ instance->connector_specific_data = simple_connector_data;
+ struct prometheus_remote_write_specific_data *connector_specific_data =
+ mallocz(sizeof(struct prometheus_remote_write_specific_data *));
+ simple_connector_data->connector_specific_data = (void *)connector_specific_data;
+ connector_specific_data->write_request = __real_init_write_request();
+
+ expect_function_call(__wrap_simple_connector_end_batch);
+ expect_value(__wrap_simple_connector_end_batch, instance, instance);
+ will_return(__wrap_simple_connector_end_batch, 0);
+ __real_add_host_info(
+ connector_specific_data->write_request,
+ "test_name", "test_instance", "test_application", "test_version", 15051);
+
+ __real_add_label(connector_specific_data->write_request, "test_key", "test_value");
+
+ __real_add_metric(
+ connector_specific_data->write_request,
+ "test_name", "test chart", "test_family", "test_dimension", "test_instance",
+ 123000321, 15052);
+
+ assert_int_equal(format_batch_prometheus_remote_write(instance), 0);
+
+ BUFFER *buffer = instance->buffer;
+ assert_int_equal(buffer_strlen(buffer), 192);
+
+ BUFFER *escaped_buffer = buffer_create(850);
+ size_t len = buffer_strlen(buffer);
+ char *ch = (char *)buffer_tostring(buffer);
+ for (; len > 0; ch++, len--)
+ buffer_sprintf(escaped_buffer, "\\%03o", (unsigned int)*ch);
+ assert_string_equal(
+ buffer_tostring(escaped_buffer),
+ "\\37777777641\\002\\120\\012\\37777777622\\001\\012\\025\\012\\010\\137\\137\\156\\141\\155\\145\\137\\137"
+ "\\022\\011\\164\\145\\163\\164\\005\\015\\064\\012\\031\\012\\010\\151\\156\\163\\164\\141\\156\\143\\145\\022"
+ "\\015\\005\\027\\021\\017\\100\\012\\037\\012\\013\\141\\160\\160\\154\\151\\143\\141\\164\\151\\157\\156\\022"
+ "\\020\\005\\036\\035\\022\\034\\012\\027\\012\\007\\166\\145\\162\\163\\001\\035\\000\\014\\005\\035\\015\\016"
+ "\\014\\012\\026\\012\\010\\005\\020\\020\\153\\145\\171\\022\\012\\005\\012\\040\\166\\141\\154\\165\\145\\022"
+ "\\014\\011\\000\\005\\001\\030\\37777777760\\077\\020\\37777777713\\165\\012\\37777777611\\142\\37777777625"
+ "\\000\\034\\023\\012\\005\\143\\150\\141\\162\\164\\011\\075\\000\\040\\005\\014\\054\\012\\025\\012\\006\\146"
+ "\\141\\155\\151\\154\\171\\022\\013\\005\\123\\011\\015\\040\\012\\033\\012\\011\\144\\151\\155\\145\\156\\005"
+ "\\37777777607\\000\\016\\005\\032\\025\\020\\000\\012\\146\\37777777736\\000\\064\\022\\014\\011\\000\\000\\000"
+ "\\004\\130\\123\\37777777635\\101\\020\\37777777714\\165");
+
+ buffer_free(escaped_buffer);
+ protocol_buffers_shutdown();
+}
+#endif // ENABLE_PROMETHEUS_REMOTE_WRITE
+
+#if HAVE_KINESIS
+static void test_init_aws_kinesis_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+
+ struct aws_kinesis_specific_config *connector_specific_config =
+ callocz(1, sizeof(struct aws_kinesis_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->stream_name = strdupz("test_stream");
+ connector_specific_config->auth_key_id = strdupz("test_auth_key_id");
+ connector_specific_config->secure_key = strdupz("test_secure_key");
+
+ expect_function_call(__wrap_aws_sdk_init);
+ expect_function_call(__wrap_kinesis_init);
+ expect_not_value(__wrap_kinesis_init, kinesis_specific_data_p, NULL);
+ expect_string(__wrap_kinesis_init, region, "localhost");
+ expect_string(__wrap_kinesis_init, access_key_id, "test_auth_key_id");
+ expect_string(__wrap_kinesis_init, secret_key, "test_secure_key");
+ expect_value(__wrap_kinesis_init, timeout, 10000);
+
+ assert_int_equal(init_aws_kinesis_instance(instance), 0);
+
+ assert_ptr_equal(instance->worker, aws_kinesis_connector_worker);
+ assert_ptr_equal(instance->start_batch_formatting, NULL);
+ assert_ptr_equal(instance->start_host_formatting, format_host_labels_json_plaintext);
+ assert_ptr_equal(instance->start_chart_formatting, NULL);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_json_plaintext);
+ assert_ptr_equal(instance->end_chart_formatting, NULL);
+ assert_ptr_equal(instance->end_host_formatting, flush_host_labels);
+ assert_ptr_equal(instance->end_batch_formatting, NULL);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+ assert_ptr_not_equal(instance->connector_specific_data, NULL);
+ freez(instance->connector_specific_data);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+
+ expect_function_call(__wrap_kinesis_init);
+ expect_not_value(__wrap_kinesis_init, kinesis_specific_data_p, NULL);
+ expect_string(__wrap_kinesis_init, region, "localhost");
+ expect_string(__wrap_kinesis_init, access_key_id, "test_auth_key_id");
+ expect_string(__wrap_kinesis_init, secret_key, "test_secure_key");
+ expect_value(__wrap_kinesis_init, timeout, 10000);
+
+ assert_int_equal(init_aws_kinesis_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_json_plaintext);
+
+ free(connector_specific_config->stream_name);
+ free(connector_specific_config->auth_key_id);
+ free(connector_specific_config->secure_key);
+}
+
+static void test_aws_kinesis_connector_worker(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+ BUFFER *buffer = instance->buffer;
+
+ __real_mark_scheduled_instances(engine);
+
+ expect_function_call(__wrap_rrdhost_is_exportable);
+ expect_value(__wrap_rrdhost_is_exportable, instance, instance);
+ expect_value(__wrap_rrdhost_is_exportable, host, localhost);
+ will_return(__wrap_rrdhost_is_exportable, 1);
+
+ RRDSET *st = localhost->rrdset_root;
+ expect_function_call(__wrap_rrdset_is_exportable);
+ expect_value(__wrap_rrdset_is_exportable, instance, instance);
+ expect_value(__wrap_rrdset_is_exportable, st, st);
+ will_return(__wrap_rrdset_is_exportable, 1);
+
+ expect_function_call(__wrap_simple_connector_end_batch);
+ expect_value(__wrap_simple_connector_end_batch, instance, instance);
+ will_return(__wrap_simple_connector_end_batch, 0);
+ __real_prepare_buffers(engine);
+
+ struct aws_kinesis_specific_config *connector_specific_config =
+ callocz(1, sizeof(struct aws_kinesis_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->stream_name = strdupz("test_stream");
+ connector_specific_config->auth_key_id = strdupz("test_auth_key_id");
+ connector_specific_config->secure_key = strdupz("test_secure_key");
+
+ struct aws_kinesis_specific_data *connector_specific_data = callocz(1, sizeof(struct aws_kinesis_specific_data));
+ instance->connector_specific_data = (void *)connector_specific_data;
+
+ expect_function_call(__wrap_kinesis_put_record);
+ expect_not_value(__wrap_kinesis_put_record, kinesis_specific_data_p, NULL);
+ expect_string(__wrap_kinesis_put_record, stream_name, "test_stream");
+ expect_string(__wrap_kinesis_put_record, partition_key, "netdata_0");
+ expect_value(__wrap_kinesis_put_record, data, buffer_tostring(buffer));
+ // The buffer is prepared by Graphite exporting connector
+ expect_string(
+ __wrap_kinesis_put_record, data,
+ "netdata.test-host.chart_name.dimension_name;TAG1=VALUE1 TAG2=VALUE2 123000321 15051\n");
+ expect_value(__wrap_kinesis_put_record, data_len, 84);
+
+ expect_function_call(__wrap_kinesis_get_result);
+ expect_value(__wrap_kinesis_get_result, request_outcomes_p, NULL);
+ expect_not_value(__wrap_kinesis_get_result, error_message, NULL);
+ expect_not_value(__wrap_kinesis_get_result, sent_bytes, NULL);
+ expect_not_value(__wrap_kinesis_get_result, lost_bytes, NULL);
+ will_return(__wrap_kinesis_get_result, 0);
+
+ expect_function_call(__wrap_send_internal_metrics);
+ expect_value(__wrap_send_internal_metrics, instance, instance);
+ will_return(__wrap_send_internal_metrics, 0);
+
+ aws_kinesis_connector_worker(instance);
+
+ assert_int_equal(stats->buffered_metrics, 0);
+ assert_int_equal(stats->buffered_bytes, 84);
+ assert_int_equal(stats->received_bytes, 0);
+ assert_int_equal(stats->sent_bytes, 84);
+ assert_int_equal(stats->sent_metrics, 1);
+ assert_int_equal(stats->lost_metrics, 0);
+ assert_int_equal(stats->receptions, 1);
+ assert_int_equal(stats->transmission_successes, 1);
+ assert_int_equal(stats->transmission_failures, 0);
+ assert_int_equal(stats->data_lost_events, 0);
+ assert_int_equal(stats->lost_bytes, 0);
+ assert_int_equal(stats->reconnects, 0);
+
+ free(connector_specific_config->stream_name);
+ free(connector_specific_config->auth_key_id);
+ free(connector_specific_config->secure_key);
+}
+#endif // HAVE_KINESIS
+
+#if ENABLE_EXPORTING_PUBSUB
+static void test_init_pubsub_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+
+ struct pubsub_specific_config *connector_specific_config =
+ callocz(1, sizeof(struct pubsub_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->credentials_file = strdupz("/test/credentials/file");
+ connector_specific_config->project_id = strdupz("test_project_id");
+ connector_specific_config->topic_id = strdupz("test_topic_id");
+
+ expect_function_call(__wrap_pubsub_init);
+ expect_not_value(__wrap_pubsub_init, pubsub_specific_data_p, NULL);
+ expect_string(__wrap_pubsub_init, destination, "localhost");
+ expect_string(__wrap_pubsub_init, error_message, "");
+ expect_string(__wrap_pubsub_init, credentials_file, "/test/credentials/file");
+ expect_string(__wrap_pubsub_init, project_id, "test_project_id");
+ expect_string(__wrap_pubsub_init, topic_id, "test_topic_id");
+ will_return(__wrap_pubsub_init, 0);
+
+ assert_int_equal(init_pubsub_instance(instance), 0);
+
+ assert_ptr_equal(instance->worker, pubsub_connector_worker);
+ assert_ptr_equal(instance->start_batch_formatting, NULL);
+ assert_ptr_equal(instance->start_host_formatting, format_host_labels_json_plaintext);
+ assert_ptr_equal(instance->start_chart_formatting, NULL);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_json_plaintext);
+ assert_ptr_equal(instance->end_chart_formatting, NULL);
+ assert_ptr_equal(instance->end_host_formatting, flush_host_labels);
+ assert_ptr_equal(instance->end_batch_formatting, NULL);
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+ assert_ptr_not_equal(instance->connector_specific_data, NULL);
+ freez(instance->connector_specific_data);
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
+
+ expect_function_call(__wrap_pubsub_init);
+ expect_not_value(__wrap_pubsub_init, pubsub_specific_data_p, NULL);
+ expect_string(__wrap_pubsub_init, destination, "localhost");
+ expect_string(__wrap_pubsub_init, error_message, "");
+ expect_string(__wrap_pubsub_init, credentials_file, "/test/credentials/file");
+ expect_string(__wrap_pubsub_init, project_id, "test_project_id");
+ expect_string(__wrap_pubsub_init, topic_id, "test_topic_id");
+ will_return(__wrap_pubsub_init, 0);
+
+ assert_int_equal(init_pubsub_instance(instance), 0);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_stored_json_plaintext);
+
+ free(connector_specific_config->credentials_file);
+ free(connector_specific_config->project_id);
+ free(connector_specific_config->topic_id);
+}
+
+static void test_pubsub_connector_worker(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+
+ __real_mark_scheduled_instances(engine);
+
+ expect_function_call(__wrap_rrdhost_is_exportable);
+ expect_value(__wrap_rrdhost_is_exportable, instance, instance);
+ expect_value(__wrap_rrdhost_is_exportable, host, localhost);
+ will_return(__wrap_rrdhost_is_exportable, 1);
+
+ RRDSET *st = localhost->rrdset_root;
+ expect_function_call(__wrap_rrdset_is_exportable);
+ expect_value(__wrap_rrdset_is_exportable, instance, instance);
+ expect_value(__wrap_rrdset_is_exportable, st, st);
+ will_return(__wrap_rrdset_is_exportable, 1);
+
+ expect_function_call(__wrap_simple_connector_end_batch);
+ expect_value(__wrap_simple_connector_end_batch, instance, instance);
+ will_return(__wrap_simple_connector_end_batch, 0);
+ __real_prepare_buffers(engine);
+
+ struct pubsub_specific_config *connector_specific_config =
+ callocz(1, sizeof(struct pubsub_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->credentials_file = strdupz("/test/credentials/file");
+ connector_specific_config->project_id = strdupz("test_project_id");
+ connector_specific_config->topic_id = strdupz("test_topic_id");
+
+ struct pubsub_specific_data *connector_specific_data = callocz(1, sizeof(struct pubsub_specific_data));
+ instance->connector_specific_data = (void *)connector_specific_data;
+
+ expect_function_call(__wrap_pubsub_add_message);
+ expect_not_value(__wrap_pubsub_add_message, pubsub_specific_data_p, NULL);
+ // The buffer is prepared by Graphite exporting connector
+ expect_string(
+ __wrap_pubsub_add_message, data,
+ "netdata.test-host.chart_name.dimension_name;TAG1=VALUE1 TAG2=VALUE2 123000321 15051\n");
+ will_return(__wrap_pubsub_add_message, 0);
+
+ expect_function_call(__wrap_pubsub_publish);
+ expect_not_value(__wrap_pubsub_publish, pubsub_specific_data_p, NULL);
+ expect_string(__wrap_pubsub_publish, error_message, "");
+ expect_value(__wrap_pubsub_publish, buffered_metrics, 1);
+ expect_value(__wrap_pubsub_publish, buffered_bytes, 84);
+ will_return(__wrap_pubsub_publish, 0);
+
+ expect_function_call(__wrap_pubsub_get_result);
+ expect_not_value(__wrap_pubsub_get_result, pubsub_specific_data_p, NULL);
+ expect_not_value(__wrap_pubsub_get_result, error_message, NULL);
+ expect_not_value(__wrap_pubsub_get_result, sent_metrics, NULL);
+ expect_not_value(__wrap_pubsub_get_result, sent_bytes, NULL);
+ expect_not_value(__wrap_pubsub_get_result, lost_metrics, NULL);
+ expect_not_value(__wrap_pubsub_get_result, lost_bytes, NULL);
+ will_return(__wrap_pubsub_get_result, 0);
+
+ expect_function_call(__wrap_send_internal_metrics);
+ expect_value(__wrap_send_internal_metrics, instance, instance);
+ will_return(__wrap_send_internal_metrics, 0);
+
+ pubsub_connector_worker(instance);
+
+ assert_int_equal(stats->buffered_metrics, 0);
+ assert_int_equal(stats->buffered_bytes, 84);
+ assert_int_equal(stats->received_bytes, 0);
+ assert_int_equal(stats->sent_bytes, 84);
+ assert_int_equal(stats->sent_metrics, 0);
+ assert_int_equal(stats->lost_metrics, 0);
+ assert_int_equal(stats->receptions, 1);
+ assert_int_equal(stats->transmission_successes, 1);
+ assert_int_equal(stats->transmission_failures, 0);
+ assert_int_equal(stats->data_lost_events, 0);
+ assert_int_equal(stats->lost_bytes, 0);
+ assert_int_equal(stats->reconnects, 0);
+
+ free(connector_specific_config->credentials_file);
+ free(connector_specific_config->project_id);
+ free(connector_specific_config->topic_id);
+}
+#endif // ENABLE_EXPORTING_PUBSUB
+
+#if HAVE_MONGOC
+static void test_init_mongodb_instance(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ instance->config.options = EXPORTING_SOURCE_DATA_AS_COLLECTED | EXPORTING_OPTION_SEND_NAMES;
+
+ struct mongodb_specific_config *connector_specific_config = callocz(1, sizeof(struct mongodb_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->database = strdupz("test_database");
+ connector_specific_config->collection = strdupz("test_collection");
+ instance->config.buffer_on_failures = 10;
+
+ expect_function_call(__wrap_mongoc_init);
+ expect_function_call(__wrap_mongoc_uri_new_with_error);
+ expect_string(__wrap_mongoc_uri_new_with_error, uri_string, "localhost");
+ expect_not_value(__wrap_mongoc_uri_new_with_error, error, NULL);
+ will_return(__wrap_mongoc_uri_new_with_error, 0xf1);
+
+ expect_function_call(__wrap_mongoc_uri_get_option_as_int32);
+ expect_value(__wrap_mongoc_uri_get_option_as_int32, uri, 0xf1);
+ expect_string(__wrap_mongoc_uri_get_option_as_int32, option, MONGOC_URI_SOCKETTIMEOUTMS);
+ expect_value(__wrap_mongoc_uri_get_option_as_int32, fallback, 1000);
+ will_return(__wrap_mongoc_uri_get_option_as_int32, 1000);
+
+ expect_function_call(__wrap_mongoc_uri_set_option_as_int32);
+ expect_value(__wrap_mongoc_uri_set_option_as_int32, uri, 0xf1);
+ expect_string(__wrap_mongoc_uri_set_option_as_int32, option, MONGOC_URI_SOCKETTIMEOUTMS);
+ expect_value(__wrap_mongoc_uri_set_option_as_int32, value, 1000);
+ will_return(__wrap_mongoc_uri_set_option_as_int32, true);
+
+ expect_function_call(__wrap_mongoc_client_new_from_uri);
+ expect_value(__wrap_mongoc_client_new_from_uri, uri, 0xf1);
+ will_return(__wrap_mongoc_client_new_from_uri, 0xf2);
+
+ expect_function_call(__wrap_mongoc_client_set_appname);
+ expect_value(__wrap_mongoc_client_set_appname, client, 0xf2);
+ expect_string(__wrap_mongoc_client_set_appname, appname, "netdata");
+ will_return(__wrap_mongoc_client_set_appname, true);
+
+ expect_function_call(__wrap_mongoc_client_get_collection);
+ expect_value(__wrap_mongoc_client_get_collection, client, 0xf2);
+ expect_string(__wrap_mongoc_client_get_collection, db, "test_database");
+ expect_string(__wrap_mongoc_client_get_collection, collection, "test_collection");
+ will_return(__wrap_mongoc_client_get_collection, 0xf3);
+
+ expect_function_call(__wrap_mongoc_uri_destroy);
+ expect_value(__wrap_mongoc_uri_destroy, uri, 0xf1);
+
+ assert_int_equal(init_mongodb_instance(instance), 0);
+
+ assert_ptr_equal(instance->worker, mongodb_connector_worker);
+ assert_ptr_equal(instance->start_batch_formatting, NULL);
+ assert_ptr_equal(instance->start_host_formatting, format_host_labels_json_plaintext);
+ assert_ptr_equal(instance->start_chart_formatting, NULL);
+ assert_ptr_equal(instance->metric_formatting, format_dimension_collected_json_plaintext);
+ assert_ptr_equal(instance->end_chart_formatting, NULL);
+ assert_ptr_equal(instance->end_host_formatting, flush_host_labels);
+ assert_ptr_equal(instance->end_batch_formatting, format_batch_mongodb);
+ assert_ptr_equal(instance->prepare_header, NULL);
+ assert_ptr_equal(instance->check_response, NULL);
+
+ assert_ptr_not_equal(instance->buffer, NULL);
+ buffer_free(instance->buffer);
+
+ assert_ptr_not_equal(instance->connector_specific_data, NULL);
+
+ struct mongodb_specific_data *connector_specific_data =
+ (struct mongodb_specific_data *)instance->connector_specific_data;
+ size_t number_of_buffers = 1;
+ struct bson_buffer *current_buffer = connector_specific_data->first_buffer;
+ while (current_buffer->next != connector_specific_data->first_buffer) {
+ current_buffer = current_buffer->next;
+ number_of_buffers++;
+ if (number_of_buffers == (size_t)(instance->config.buffer_on_failures + 1)) {
+ number_of_buffers = 0;
+ break;
+ }
+ }
+ assert_int_equal(number_of_buffers, 9);
+
+ free(connector_specific_config->database);
+ free(connector_specific_config->collection);
+}
+
+static void test_format_batch_mongodb(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+ struct stats *stats = &instance->stats;
+
+ struct mongodb_specific_data *connector_specific_data = mallocz(sizeof(struct mongodb_specific_data));
+ instance->connector_specific_data = (void *)connector_specific_data;
+
+ struct bson_buffer *current_buffer = callocz(1, sizeof(struct bson_buffer));
+ connector_specific_data->first_buffer = current_buffer;
+ connector_specific_data->first_buffer->next = current_buffer;
+ connector_specific_data->last_buffer = current_buffer;
+
+ BUFFER *buffer = buffer_create(0);
+ buffer_sprintf(buffer, "{ \"metric\": \"test_metric\" }\n");
+ instance->buffer = buffer;
+ stats->buffered_metrics = 1;
+
+ assert_int_equal(format_batch_mongodb(instance), 0);
+
+ assert_int_equal(connector_specific_data->last_buffer->documents_inserted, 1);
+ assert_int_equal(buffer_strlen(buffer), 0);
+
+ size_t len;
+ char *str = bson_as_canonical_extended_json(connector_specific_data->last_buffer->insert[0], &len);
+ assert_string_equal(str, "{ \"metric\" : \"test_metric\" }");
+
+ freez(str);
+ buffer_free(buffer);
+}
+
+static void test_mongodb_connector_worker(void **state)
+{
+ struct engine *engine = *state;
+ struct instance *instance = engine->instance_root;
+
+ struct mongodb_specific_config *connector_specific_config = callocz(1, sizeof(struct mongodb_specific_config));
+ instance->config.connector_specific_config = connector_specific_config;
+ connector_specific_config->database = strdupz("test_database");
+
+ struct mongodb_specific_data *connector_specific_data = callocz(1, sizeof(struct mongodb_specific_data));
+ instance->connector_specific_data = (void *)connector_specific_data;
+ connector_specific_config->collection = strdupz("test_collection");
+
+ struct bson_buffer *buffer = callocz(1, sizeof(struct bson_buffer));
+ buffer->documents_inserted = 1;
+ connector_specific_data->first_buffer = buffer;
+ connector_specific_data->first_buffer->next = buffer;
+
+ connector_specific_data->first_buffer->insert = callocz(1, sizeof(bson_t *));
+ bson_error_t bson_error;
+ connector_specific_data->first_buffer->insert[0] =
+ bson_new_from_json((const uint8_t *)"{ \"test_key\" : \"test_value\" }", -1, &bson_error);
+
+ connector_specific_data->client = mongoc_client_new("mongodb://localhost");
+ connector_specific_data->collection =
+ __real_mongoc_client_get_collection(connector_specific_data->client, "test_database", "test_collection");
+
+ expect_function_call(__wrap_mongoc_collection_insert_many);
+ expect_value(__wrap_mongoc_collection_insert_many, collection, connector_specific_data->collection);
+ expect_value(__wrap_mongoc_collection_insert_many, documents, connector_specific_data->first_buffer->insert);
+ expect_value(__wrap_mongoc_collection_insert_many, n_documents, 1);
+ expect_value(__wrap_mongoc_collection_insert_many, opts, NULL);
+ expect_value(__wrap_mongoc_collection_insert_many, reply, NULL);
+ expect_not_value(__wrap_mongoc_collection_insert_many, error, NULL);
+ will_return(__wrap_mongoc_collection_insert_many, true);
+
+ expect_function_call(__wrap_send_internal_metrics);
+ expect_value(__wrap_send_internal_metrics, instance, instance);
+ will_return(__wrap_send_internal_metrics, 0);
+
+ mongodb_connector_worker(instance);
+
+ assert_ptr_equal(connector_specific_data->first_buffer->insert, NULL);
+ assert_int_equal(connector_specific_data->first_buffer->documents_inserted, 0);
+ assert_ptr_equal(connector_specific_data->first_buffer, connector_specific_data->first_buffer->next);
+
+ struct stats *stats = &instance->stats;
+ assert_int_equal(stats->buffered_metrics, 0);
+ assert_int_equal(stats->buffered_bytes, 0);
+ assert_int_equal(stats->received_bytes, 0);
+ assert_int_equal(stats->sent_bytes, 30);
+ assert_int_equal(stats->sent_metrics, 1);
+ assert_int_equal(stats->lost_metrics, 0);
+ assert_int_equal(stats->receptions, 1);
+ assert_int_equal(stats->transmission_successes, 1);
+ assert_int_equal(stats->transmission_failures, 0);
+ assert_int_equal(stats->data_lost_events, 0);
+ assert_int_equal(stats->lost_bytes, 0);
+ assert_int_equal(stats->reconnects, 0);
+
+ free(connector_specific_config->database);
+ free(connector_specific_config->collection);
+}
+#endif // HAVE_MONGOC
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_exporting_engine, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test(test_read_exporting_config),
+ cmocka_unit_test_setup_teardown(test_init_connectors, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_init_graphite_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_init_json_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_init_opentsdb_telnet_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_init_opentsdb_http_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_mark_scheduled_instances, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_rrdhost_is_exportable, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_false_rrdhost_is_exportable, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_rrdset_is_exportable, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_false_rrdset_is_exportable, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_exporting_calculate_value_from_stored_data, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(test_prepare_buffers, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test(test_exporting_name_copy),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_collected_graphite_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_stored_graphite_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_collected_json_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_stored_json_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_collected_opentsdb_telnet, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_stored_opentsdb_telnet, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_collected_opentsdb_http, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_stored_opentsdb_http, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_exporting_discard_response, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_simple_connector_receive_response, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_simple_connector_send_buffer, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_simple_connector_worker, setup_initialized_engine, teardown_initialized_engine),
+ };
+
+ const struct CMUnitTest label_tests[] = {
+ cmocka_unit_test(test_sanitize_json_string),
+ cmocka_unit_test(test_sanitize_graphite_label_value),
+ cmocka_unit_test(test_sanitize_opentsdb_label_value),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_labels_json_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_labels_graphite_plaintext, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_labels_opentsdb_telnet, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_labels_opentsdb_http, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(test_flush_host_labels, setup_initialized_engine, teardown_initialized_engine),
+ };
+
+ int test_res = cmocka_run_group_tests_name("exporting_engine", tests, NULL, NULL) +
+ cmocka_run_group_tests_name("labels_in_exporting_engine", label_tests, NULL, NULL);
+
+ const struct CMUnitTest internal_metrics_tests[] = {
+ cmocka_unit_test_setup_teardown(test_create_main_rusage_chart, setup_rrdhost, teardown_rrdhost),
+ cmocka_unit_test(test_send_main_rusage),
+ cmocka_unit_test(test_send_internal_metrics),
+ };
+
+ test_res += cmocka_run_group_tests_name("internal_metrics", internal_metrics_tests, NULL, NULL);
+
+ const struct CMUnitTest prometheus_web_api_tests[] = {
+ cmocka_unit_test_setup_teardown(test_can_send_rrdset, setup_prometheus, teardown_prometheus),
+ cmocka_unit_test_setup_teardown(test_prometheus_name_copy, setup_prometheus, teardown_prometheus),
+ cmocka_unit_test_setup_teardown(test_prometheus_label_copy, setup_prometheus, teardown_prometheus),
+ cmocka_unit_test_setup_teardown(test_prometheus_units_copy, setup_prometheus, teardown_prometheus),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_labels_prometheus, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ rrd_stats_api_v1_charts_allmetrics_prometheus, setup_prometheus, teardown_prometheus),
+ };
+
+ test_res += cmocka_run_group_tests_name("prometheus_web_api", prometheus_web_api_tests, NULL, NULL);
+
+#if ENABLE_PROMETHEUS_REMOTE_WRITE
+ const struct CMUnitTest prometheus_remote_write_tests[] = {
+ cmocka_unit_test_setup_teardown(
+ test_init_prometheus_remote_write_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_prometheus_remote_write_prepare_header, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test(test_process_prometheus_remote_write_response),
+ cmocka_unit_test_setup_teardown(
+ test_format_host_prometheus_remote_write, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_dimension_prometheus_remote_write, setup_initialized_engine, teardown_initialized_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_batch_prometheus_remote_write, setup_initialized_engine, teardown_initialized_engine),
+ };
+
+ test_res += cmocka_run_group_tests_name(
+ "prometheus_remote_write_exporting_connector", prometheus_remote_write_tests, NULL, NULL);
+#endif
+
+#if HAVE_KINESIS
+ const struct CMUnitTest kinesis_tests[] = {
+ cmocka_unit_test_setup_teardown(
+ test_init_aws_kinesis_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_aws_kinesis_connector_worker, setup_initialized_engine, teardown_initialized_engine),
+ };
+
+ test_res += cmocka_run_group_tests_name("kinesis_exporting_connector", kinesis_tests, NULL, NULL);
+#endif
+
+#if ENABLE_EXPORTING_PUBSUB
+ const struct CMUnitTest pubsub_tests[] = {
+ cmocka_unit_test_setup_teardown(
+ test_init_pubsub_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_pubsub_connector_worker, setup_initialized_engine, teardown_initialized_engine),
+ };
+
+ test_res += cmocka_run_group_tests_name("pubsub_exporting_connector", pubsub_tests, NULL, NULL);
+#endif
+
+#if HAVE_MONGOC
+ const struct CMUnitTest mongodb_tests[] = {
+ cmocka_unit_test_setup_teardown(
+ test_init_mongodb_instance, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_format_batch_mongodb, setup_configured_engine, teardown_configured_engine),
+ cmocka_unit_test_setup_teardown(
+ test_mongodb_connector_worker, setup_configured_engine, teardown_configured_engine),
+ };
+
+ test_res += cmocka_run_group_tests_name("mongodb_exporting_connector", mongodb_tests, NULL, NULL);
+#endif
+
+ return test_res;
+}
diff --git a/exporting/tests/test_exporting_engine.h b/exporting/tests/test_exporting_engine.h
new file mode 100644
index 000000000..800be1b99
--- /dev/null
+++ b/exporting/tests/test_exporting_engine.h
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef TEST_EXPORTING_ENGINE_H
+#define TEST_EXPORTING_ENGINE_H 1
+
+#include "libnetdata/libnetdata.h"
+
+#include "exporting/exporting_engine.h"
+#include "exporting/graphite/graphite.h"
+#include "exporting/json/json.h"
+#include "exporting/opentsdb/opentsdb.h"
+
+#if ENABLE_PROMETHEUS_REMOTE_WRITE
+#include "exporting/prometheus/remote_write/remote_write.h"
+#endif
+
+#if HAVE_KINESIS
+#include "exporting/aws_kinesis/aws_kinesis.h"
+#endif
+
+#if ENABLE_EXPORTING_PUBSUB
+#include "exporting/pubsub/pubsub.h"
+#endif
+
+#if HAVE_MONGOC
+#include "exporting/mongodb/mongodb.h"
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <cmocka.h>
+
+#define MAX_LOG_LINE 1024
+extern char log_line[];
+
+// -----------------------------------------------------------------------
+// doubles for Netdata functions
+
+const char *__wrap_strdupz(const char *s);
+void __wrap_info_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...);
+int __wrap_connect_to_one_of(
+ const char *destination,
+ int default_port,
+ struct timeval *timeout,
+ size_t *reconnects_counter,
+ char *connected_to,
+ size_t connected_to_size);
+void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line);
+void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line);
+time_t __mock_rrddim_query_oldest_time(RRDDIM *rd);
+time_t __mock_rrddim_query_latest_time(RRDDIM *rd);
+void __mock_rrddim_query_init(RRDDIM *rd, struct rrddim_query_handle *handle, time_t start_time, time_t end_time);
+int __mock_rrddim_query_is_finished(struct rrddim_query_handle *handle);
+storage_number __mock_rrddim_query_next_metric(struct rrddim_query_handle *handle, time_t *current_time);
+void __mock_rrddim_query_finalize(struct rrddim_query_handle *handle);
+
+// -----------------------------------------------------------------------
+// wraps for system functions
+
+void __wrap_uv_thread_create(uv_thread_t thread, void (*worker)(void *arg), void *arg);
+void __wrap_uv_mutex_lock(uv_mutex_t *mutex);
+void __wrap_uv_mutex_unlock(uv_mutex_t *mutex);
+void __wrap_uv_cond_signal(uv_cond_t *cond_var);
+void __wrap_uv_cond_wait(uv_cond_t *cond_var, uv_mutex_t *mutex);
+ssize_t __wrap_recv(int sockfd, void *buf, size_t len, int flags);
+ssize_t __wrap_send(int sockfd, const void *buf, size_t len, int flags);
+
+// -----------------------------------------------------------------------
+// doubles and originals for exporting engine functions
+
+struct engine *__real_read_exporting_config();
+struct engine *__wrap_read_exporting_config();
+struct engine *__mock_read_exporting_config();
+
+int __real_init_connectors(struct engine *engine);
+int __wrap_init_connectors(struct engine *engine);
+
+int __real_mark_scheduled_instances(struct engine *engine);
+int __wrap_mark_scheduled_instances(struct engine *engine);
+
+calculated_number __real_exporting_calculate_value_from_stored_data(
+ struct instance *instance,
+ RRDDIM *rd,
+ time_t *last_timestamp);
+calculated_number __wrap_exporting_calculate_value_from_stored_data(
+ struct instance *instance,
+ RRDDIM *rd,
+ time_t *last_timestamp);
+
+int __real_prepare_buffers(struct engine *engine);
+int __wrap_prepare_buffers(struct engine *engine);
+
+void __real_create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_system);
+void __wrap_create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_system);
+
+void __real_send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system);
+void __wrap_send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system);
+
+int __real_send_internal_metrics(struct instance *instance);
+int __wrap_send_internal_metrics(struct instance *instance);
+
+int __real_rrdhost_is_exportable(struct instance *instance, RRDHOST *host);
+int __wrap_rrdhost_is_exportable(struct instance *instance, RRDHOST *host);
+
+int __real_rrdset_is_exportable(struct instance *instance, RRDSET *st);
+int __wrap_rrdset_is_exportable(struct instance *instance, RRDSET *st);
+
+int __mock_start_batch_formatting(struct instance *instance);
+int __mock_start_host_formatting(struct instance *instance, RRDHOST *host);
+int __mock_start_chart_formatting(struct instance *instance, RRDSET *st);
+int __mock_metric_formatting(struct instance *instance, RRDDIM *rd);
+int __mock_end_chart_formatting(struct instance *instance, RRDSET *st);
+int __mock_end_host_formatting(struct instance *instance, RRDHOST *host);
+int __mock_end_batch_formatting(struct instance *instance);
+
+int __wrap_simple_connector_end_batch(struct instance *instance);
+
+#if ENABLE_PROMETHEUS_REMOTE_WRITE
+void *__real_init_write_request();
+void *__wrap_init_write_request();
+
+void __real_add_host_info(
+ void *write_request_p,
+ const char *name, const char *instance, const char *application, const char *version, const int64_t timestamp);
+void __wrap_add_host_info(
+ void *write_request_p,
+ const char *name, const char *instance, const char *application, const char *version, const int64_t timestamp);
+
+void __real_add_label(void *write_request_p, char *key, char *value);
+void __wrap_add_label(void *write_request_p, char *key, char *value);
+
+void __real_add_metric(
+ void *write_request_p,
+ const char *name, const char *chart, const char *family, const char *dimension,
+ const char *instance, const double value, const int64_t timestamp);
+void __wrap_add_metric(
+ void *write_request_p,
+ const char *name, const char *chart, const char *family, const char *dimension,
+ const char *instance, const double value, const int64_t timestamp);
+#endif /* ENABLE_PROMETHEUS_REMOTE_WRITE */
+
+#if HAVE_KINESIS
+void __wrap_aws_sdk_init();
+void __wrap_kinesis_init(
+ void *kinesis_specific_data_p, const char *region, const char *access_key_id, const char *secret_key,
+ const long timeout);
+void __wrap_kinesis_put_record(
+ void *kinesis_specific_data_p, const char *stream_name, const char *partition_key, const char *data,
+ size_t data_len);
+int __wrap_kinesis_get_result(void *request_outcomes_p, char *error_message, size_t *sent_bytes, size_t *lost_bytes);
+#endif /* HAVE_KINESIS */
+
+#if ENABLE_EXPORTING_PUBSUB
+int __wrap_pubsub_init(
+ void *pubsub_specific_data_p, char *error_message, const char *destination, const char *credentials_file,
+ const char *project_id, const char *topic_id);
+int __wrap_pubsub_add_message(void *pubsub_specific_data_p, char *data);
+int __wrap_pubsub_publish(
+ void *pubsub_specific_data_p, char *error_message, size_t buffered_metrics, size_t buffered_bytes);
+int __wrap_pubsub_get_result(
+ void *pubsub_specific_data_p, char *error_message,
+ size_t *sent_metrics, size_t *sent_bytes, size_t *lost_metrics, size_t *lost_bytes);
+#endif /* ENABLE_EXPORTING_PUBSUB */
+
+#if HAVE_MONGOC
+void __wrap_mongoc_init();
+mongoc_uri_t *__wrap_mongoc_uri_new_with_error(const char *uri_string, bson_error_t *error);
+int32_t __wrap_mongoc_uri_get_option_as_int32(const mongoc_uri_t *uri, const char *option, int32_t fallback);
+bool __wrap_mongoc_uri_set_option_as_int32(const mongoc_uri_t *uri, const char *option, int32_t value);
+mongoc_client_t *__wrap_mongoc_client_new_from_uri(const mongoc_uri_t *uri);
+bool __wrap_mongoc_client_set_appname(mongoc_client_t *client, const char *appname);
+mongoc_collection_t *
+__wrap_mongoc_client_get_collection(mongoc_client_t *client, const char *db, const char *collection);
+mongoc_collection_t *
+__real_mongoc_client_get_collection(mongoc_client_t *client, const char *db, const char *collection);
+void __wrap_mongoc_uri_destroy(mongoc_uri_t *uri);
+bool __wrap_mongoc_collection_insert_many(
+ mongoc_collection_t *collection,
+ const bson_t **documents,
+ size_t n_documents,
+ const bson_t *opts,
+ bson_t *reply,
+ bson_error_t *error);
+#endif /* HAVE_MONGOC */
+
+// -----------------------------------------------------------------------
+// fixtures
+
+int setup_configured_engine(void **state);
+int teardown_configured_engine(void **state);
+int setup_rrdhost();
+int teardown_rrdhost();
+int setup_initialized_engine(void **state);
+int teardown_initialized_engine(void **state);
+int setup_prometheus(void **state);
+int teardown_prometheus(void **state);
+
+void init_connectors_in_tests(struct engine *engine);
+
+#endif /* TEST_EXPORTING_ENGINE_H */