From 2dd57c5bd5ddcb100beac2b675f969c06b3069e1 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Mon, 15 May 2017 14:40:56 +0200 Subject: [PATCH] curl_json plugin: Implement a unit test. Issue: #2266 --- Makefile.am | 9 ++++ src/curl_json.c | 10 ++-- src/curl_json_test.c | 121 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 src/curl_json_test.c diff --git a/Makefile.am b/Makefile.am index 1f18d231c21..d1d5071aa61 100644 --- a/Makefile.am +++ b/Makefile.am @@ -691,6 +691,15 @@ curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS) + +test_plugin_curl_json_SOURCES = src/curl_json_test.c \ + src/utils_curl_stats.c \ + src/daemon/configfile.c \ + src/daemon/types_list.c +test_plugin_curl_json_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) +test_plugin_curl_json_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) +test_plugin_curl_json_LDADD = libavltree.la liboconfig.la libplugin_mock.la $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS) +check_PROGRAMS += test_plugin_curl_json endif if BUILD_PLUGIN_CURL_XML diff --git a/src/curl_json.c b/src/curl_json.c index 26736c70047..eafc6911a1e 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -106,7 +106,11 @@ typedef unsigned int yajl_len_t; #endif static int cj_read(user_data_t *ud); -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value); +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value); + +/* cj_submit is a function pointer to cj_submit_impl, allowing the unit-test to + * overwrite which function is called. */ +static void (*cj_submit)(cj_t *, cj_key_t *, value_t *) = cj_submit_impl; static size_t cj_curl_callback(void *buf, /* {{{ */ size_t size, size_t nmemb, void *user_data) { @@ -773,7 +777,7 @@ static const char *cj_host(cj_t *db) /* {{{ */ return db->host; } /* }}} cj_host */ -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ { value_list_t vl = VALUE_LIST_INIT; @@ -797,7 +801,7 @@ static void cj_submit(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ vl.interval = db->interval; plugin_dispatch_values(&vl); -} /* }}} int cj_submit */ +} /* }}} int cj_submit_impl */ static int cj_sock_perform(cj_t *db) /* {{{ */ { diff --git a/src/curl_json_test.c b/src/curl_json_test.c new file mode 100644 index 00000000000..0f895efb821 --- /dev/null +++ b/src/curl_json_test.c @@ -0,0 +1,121 @@ +/** + * collectd - src/curl_json.c + * Copyright (C) 2017 Florian octo Forster + * + * Licensed under the same terms and conditions as src/curl_json.c. + * + * Authors: + * Florian octo Forster + **/ + +#include "curl_json.c" + +#include "testing.h" + +static void test_submit(cj_t *db, cj_key_t *key, value_t *value) { + /* hack: we repurpose db->curl to store received values. */ + c_avl_tree_t *values = (void *)db->curl; + + value_t *value_copy = calloc(1, sizeof(*value_copy)); + memmove(value_copy, value, sizeof(*value_copy)); + + assert(c_avl_insert(values, key->path, value_copy) == 0); +} + +static derive_t test_metric(cj_t *db, char const *path) { + c_avl_tree_t *values = (void *)db->curl; + + value_t *ret = NULL; + if (c_avl_get(values, path, (void *)&ret) == 0) { + return ret->derive; + } + + return NAN; +} + +static cj_t *test_setup(char *json, char *key_path) { + cj_t *db = calloc(1, sizeof(*db)); + db->yajl = yajl_alloc(&ycallbacks, +#if HAVE_YAJL_V2 + /* alloc funcs = */ NULL, +#else + /* alloc funcs = */ NULL, NULL, +#endif + /* context = */ (void *)db); + + /* hack; see above. */ + db->curl = (void *)cj_avl_create(); + + cj_key_t *key = calloc(1, sizeof(*key)); + key->magic = CJ_KEY_MAGIC; + key->path = strdup(key_path); + key->type = strdup("MAGIC"); + + assert(cj_append_key(db, key) == 0); + + db->state[0].tree = db->tree; + + cj_curl_callback(json, strlen(json), 1, db); +#if HAVE_YAJL_V2 + yajl_complete_parse(db->yajl); +#else + yajl_parse_complete(db->yajl); +#endif + + return db; +} + +static void test_teardown(cj_t *db) { + c_avl_tree_t *values = (void *)db->curl; + db->curl = NULL; + + void *key; + void *value; + while (c_avl_pick(values, &key, &value) == 0) { + /* key will be freed by cj_free. */ + free(value); + } + c_avl_destroy(values); + + yajl_free(db->yajl); + db->yajl = NULL; + + cj_free(db); +} + +DEF_TEST(parse) { + struct { + char *json; + char *key_path; + derive_t want; + } cases[] = { + {"{\"foo\":42,\"bar\":23}", "foo", 42}, + {"{\"foo\":42,\"bar\":23}", "bar", 23}, + {"{\"a\":{\"b\":{\"c\":123}}", "a/b/c", 123}, + {"{\"x\":{\"y\":{\"z\":789}}", "x/*/z", 789}, + // {"[10,11,12,13]", "0", 10}, + // {"{\"a\":[[10,11,12,13,14]]}", "a/0/0", 10}, + // {"{\"a\":[[10,11,12,13,14]]}", "a/0/1", 11}, + // {"{\"a\":[[10,11,12,13,14]]}", "a/0/2", 12}, + // {"{\"a\":[[10,11,12,13,14]]}", "a/0/3", 13}, + // {"{\"a\":[[10,11,12,13,14]]}", "a/0/4", 14}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + cj_t *db = test_setup(cases[i].json, cases[i].key_path); + + EXPECT_EQ_INT(cases[i].want, test_metric(db, cases[i].key_path)); + + test_teardown(db); + } + + return 0; +} + +int main(int argc, char **argv) { + cj_submit = test_submit; + + RUN_TEST(parse); + + END_TEST; +}