summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Lyda <kevin@ie.suberic.net>2020-12-08 07:37:13 +0000
committerGitHub <noreply@github.com>2020-12-07 23:37:13 -0800
commit72e72d422446c8f512a14dc704199014b7308b00 (patch)
treed57dc5663b8dbde011dce8f520f6e6837a1d5e0c
parent91a3de4cee1922a2dd786faa6a7d3ca8c9af570b (diff)
Histogram smoketests (#39)
-rw-r--r--promtest/CMakeLists.txt2
-rw-r--r--promtest/test/promtest.c2
-rw-r--r--promtest/test/promtest_histogram.c185
-rw-r--r--promtest/test/promtest_histogram.h29
4 files changed, 218 insertions, 0 deletions
diff --git a/promtest/CMakeLists.txt b/promtest/CMakeLists.txt
index 3c6de45..fa5d8b2 100644
--- a/promtest/CMakeLists.txt
+++ b/promtest/CMakeLists.txt
@@ -112,6 +112,8 @@ add_executable(
${test_dir}/promtest_counter.h
${test_dir}/promtest_gauge.c
${test_dir}/promtest_gauge.h
+ ${test_dir}/promtest_histogram.c
+ ${test_dir}/promtest_histogram.h
)
target_link_libraries(promtest microhttpd prom promhttp Unity parson Threads::Threads)
target_include_directories(promtest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
diff --git a/promtest/test/promtest.c b/promtest/test/promtest.c
index 4c7721b..5f1939e 100644
--- a/promtest/test/promtest.c
+++ b/promtest/test/promtest.c
@@ -16,6 +16,7 @@
#include "promtest_counter.h"
#include "promtest_gauge.h"
+#include "promtest_histogram.h"
#include "promtest_helpers.h"
#include "unity.h"
@@ -23,5 +24,6 @@ int main(int argc, const char **argv) {
UNITY_BEGIN();
RUN_TEST(promtest_counter);
RUN_TEST(promtest_gauge);
+ RUN_TEST(promtest_histogram);
return UNITY_END();
}
diff --git a/promtest/test/promtest_histogram.c b/promtest/test/promtest_histogram.c
new file mode 100644
index 0000000..ac8fd00
--- /dev/null
+++ b/promtest/test/promtest_histogram.c
@@ -0,0 +1,185 @@
+/**
+ * Copyright 2019-2020 DigitalOcean Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "promtest_histogram.h"
+
+#include <pthread.h>
+
+#include "parson.h"
+#include "prom.h"
+#include "promhttp.h"
+#include "promtest_helpers.h"
+#include "unity.h"
+
+static void *promtest_histogram_handler(void *data);
+static int promtest_parse_histogram_output(const char *output, char **value);
+prom_histogram_t *foo_histogram;
+
+/**
+ * @brief For each thread in a threadpool of 10 we increment a single histogram 1
+ * million times
+ *
+ * The purpose of this test is to check for deadlock and race conditions
+ */
+void promtest_histogram(void) {
+ if (promtest_histogram_setup()) {
+ TEST_FAIL_MESSAGE("failed to setup promtest_histogram");
+ }
+
+ void *retvals[PROMTEST_THREAD_POOL_SIZE];
+ pthread_t thread_pool[PROMTEST_THREAD_POOL_SIZE];
+
+ // Start each thread
+ for (int i = 0; i < PROMTEST_THREAD_POOL_SIZE; i++) {
+ if (pthread_create(&(thread_pool[i]), NULL, promtest_histogram_handler, NULL)) {
+ TEST_FAIL_MESSAGE("failed to create thread");
+ }
+ }
+
+ // Join each thread
+ for (int i = 0; i < PROMTEST_THREAD_POOL_SIZE; i++) {
+ if (pthread_join(thread_pool[i], (void **)&(retvals[i]))) {
+ TEST_FAIL_MESSAGE("thread failed to join");
+ }
+ }
+
+ // verify clean exit for each thread
+ for (int i = 0; i < PROMTEST_THREAD_POOL_SIZE; i++) {
+ if (*((int *)retvals[i]) != 0) {
+ TEST_FAIL_MESSAGE("thread did not exit properly");
+ }
+ }
+
+ // scrape the endpoint
+ FILE *f = popen("prom2json http://0.0.0.0:8000/metrics", "r");
+ if (f == NULL) {
+ TEST_FAIL_MESSAGE("shell out failed");
+ }
+ promtest_popen_buf_t *buf = promtest_popen_buf_new(f);
+ if (promtest_popen_buf_read(buf)) {
+ TEST_FAIL_MESSAGE("failed to scrape endpoint");
+ }
+
+ const char *output = strdup(buf->buf);
+ if (promtest_popen_buf_destroy(buf)) {
+ TEST_FAIL_MESSAGE("failed to duplicate buf");
+ }
+
+ // Parse the output
+ char *value = (char *)malloc(sizeof(char) * 100);
+ if (promtest_parse_histogram_output(output, &value)) {
+ TEST_FAIL_MESSAGE("failed to parse output");
+ }
+
+ // Assert
+ //TEST_ASSERT_EQUAL_STRING("5e+06", value);
+ TEST_ASSERT_EQUAL_STRING("5000000", value);
+ if (promtest_histogram_teardown()) {
+ TEST_FAIL_MESSAGE("failed to teardown promtest_histogram");
+ }
+
+ free(value);
+}
+
+int promtest_histogram_setup(void) {
+ // Initialize the default collector registry
+ prom_collector_registry_default_init();
+
+ // Set the histogram
+ foo_histogram =
+ prom_collector_registry_must_register_metric(prom_histogram_new("foo_histogram", "histogram for foo", prom_histogram_default_buckets, 0, NULL));
+
+ // Set the collector registry on the handler to the default registry
+ promhttp_set_active_collector_registry(NULL);
+
+ // Start the HTTP server
+ promtest_daemon = promhttp_start_daemon(MHD_USE_SELECT_INTERNALLY, 8000, NULL, NULL);
+
+ if (promtest_daemon == NULL)
+ return 1;
+ else
+ return 0;
+}
+
+int promtest_histogram_teardown(void) {
+ // Destroy the default registry. This effectively deallocates all metrics
+ // registered to it, including itself
+ prom_collector_registry_destroy(PROM_COLLECTOR_REGISTRY_DEFAULT);
+ PROM_COLLECTOR_REGISTRY_DEFAULT = NULL;
+
+ // Stop the HTTP server
+ MHD_stop_daemon(promtest_daemon);
+
+ return 0;
+}
+
+/**
+ * @brief The entrypoint to a worker thread within the prom_histogram_test
+ */
+static void *promtest_histogram_handler(void *data) {
+ for (int i = 0; i < 1000000; i++) {
+ prom_histogram_observe(foo_histogram, i/1000.0, NULL);
+ }
+ int *retval = (int *)malloc(sizeof(int));
+ *retval = 0;
+ return (void *)retval;
+}
+
+/**
+ * @brief Parse the output and set the value of the foo_histogram metric.
+ *
+ * We must past a pointer to a char* so the value gets updated
+ */
+static int promtest_parse_histogram_output(const char *output, char **value) {
+ // Parse the JSON output
+ JSON_Value *root = json_parse_string(output);
+ if (json_value_get_type(root) != JSONArray) {
+ TEST_FAIL_MESSAGE("JSON Parse error...expected JSONArray type");
+ }
+
+ JSON_Array *collection = json_value_get_array(root);
+ if (collection == NULL) {
+ TEST_FAIL_MESSAGE("Failed to extract array from JSON_Value");
+ }
+
+ for (int i = 0; i < json_array_get_count(collection); i++) {
+ JSON_Object *obj = json_array_get_object(collection, i);
+ if (obj == NULL) {
+ TEST_FAIL_MESSAGE("failed to retrieve single object within JSON_Array");
+ }
+ const char *name = json_object_get_string(obj, "name");
+ if (strcmp(name, "foo_histogram")) {
+ continue;
+ }
+ JSON_Array *samples = json_object_dotget_array(obj, "metrics");
+ if (samples == NULL) {
+ TEST_FAIL_MESSAGE("failed to retrieve metrics from JSON_Object");
+ }
+ if (json_array_get_count(samples) < 1) {
+ TEST_FAIL_MESSAGE("No samples found");
+ }
+ JSON_Object *sample = json_array_get_object(samples, 0);
+ if (sample == NULL) {
+ TEST_FAIL_MESSAGE("failed to get metric sample");
+ }
+ *value = (char *)json_object_get_string(sample, "count");
+ break;
+ }
+ if (strlen(*value) == 0) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/promtest/test/promtest_histogram.h b/promtest/test/promtest_histogram.h
new file mode 100644
index 0000000..de4ef67
--- /dev/null
+++ b/promtest/test/promtest_histogram.h
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2019-2020 DigitalOcean Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "prom.h"
+
+#ifndef PROMTEST_HISTOGRAM_H
+#define PROMTEST_HISTOGRAM_H
+
+extern prom_histogram_t *foo_histogram;
+
+int promtest_histogram_setup(void);
+int promtest_histogram_teardown(void);
+
+void promtest_histogram(void);
+
+#endif // PROMTEST_HISTOGRAM_H