diff options
Diffstat (limited to 'src/stats/test-stats-metrics.c')
-rw-r--r-- | src/stats/test-stats-metrics.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/src/stats/test-stats-metrics.c b/src/stats/test-stats-metrics.c new file mode 100644 index 0000000..c384014 --- /dev/null +++ b/src/stats/test-stats-metrics.c @@ -0,0 +1,449 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "test-stats-common.h" +#include "array.h" + +bool test_stats_callback(struct event *event, + enum event_callback_type type ATTR_UNUSED, + struct failure_context *ctx, const char *fmt ATTR_UNUSED, + va_list args ATTR_UNUSED) +{ + if (stats_metrics != NULL) { + stats_metrics_event(stats_metrics, event, ctx); + struct event_filter *filter = + stats_metrics_get_event_filter(stats_metrics); + return !event_filter_match(filter, event, ctx); + } + return TRUE; +} + +static const char *settings_blob_1 = +"metric=test\n" +"metric/test/metric_name=test\n" +"metric/test/filter=event=test\n" +"\n"; + +static void test_stats_metrics(void) +{ + test_begin("stats metrics (event counting)"); + + /* register some stats */ + test_init(settings_blob_1); + + /* push event in */ + struct event *event = event_create(NULL); + event_add_category(event, &test_category); + event_set_name(event, "test"); + test_event_send(event); + event_unref(&event); + + test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == 1); + test_assert(get_stats_dist_field("test", STATS_DIST_SUM) > 0); + + test_deinit(); + test_end(); +} + +static const char *settings_blob_2 = +"metric=test\n" +"metric/test/metric_name=test\n" +"metric/test/filter=(event=test AND test_field=value)\n" +"\n"; + +static void test_stats_metrics_filter(void) +{ + test_begin("stats metrics (filter)"); + + test_init(settings_blob_2); + + /* check filter */ + struct event_filter *filter = + stats_metrics_get_event_filter(stats_metrics); + string_t *str_filter = t_str_new(64); + event_filter_export(filter, str_filter); + test_assert_strcmp("((event=\"test\" AND \"test_field\"=\"value\"))", + str_c(str_filter)); + + /* send event */ + struct event *event = event_create(NULL); + event_add_category(event, &test_category); + event_set_name(event, "test"); + event_add_str(event, "test_field", "value"); + test_event_send(event); + event_unref(&event); + + test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == 1); + test_assert(get_stats_dist_field("test", STATS_DIST_SUM) > 0); + + /* send another event */ + event = event_create(NULL); + event_add_category(event, &test_category); + event_set_name(event, "test"); + event_add_str(event, "test_field", "nother value"); + e_debug(event, "test"); + event_unref(&event); + + test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == 1); + test_assert(get_stats_dist_field("test", STATS_DIST_SUM) > 0); + + test_deinit(); + test_end(); +} + +static void test_stats_metrics_group_by_check_one(const struct metric *metric, + const char *sub_name, + unsigned int total_count, + unsigned int submetric_count, + unsigned int group_by_count, + enum stats_metric_group_by_func group_by_func, + const char *group_by_field, + enum metric_value_type value_type) +{ + test_assert_strcmp(metric->name, "test"); + + if (sub_name != NULL) + test_assert_strcmp(metric->sub_name, sub_name); + else + test_assert(metric->sub_name == NULL); + + test_assert(stats_dist_get_count(metric->duration_stats) == total_count); + + if (submetric_count > 0) { + test_assert(array_is_created(&metric->sub_metrics)); + test_assert(array_count(&metric->sub_metrics) == submetric_count); + } else { + test_assert(!array_is_created(&metric->sub_metrics)); + } + + if (group_by_count > 0) { + test_assert(metric->group_by_count == group_by_count); + i_assert(metric->group_by != NULL); + test_assert(metric->group_by[0].func == group_by_func); + test_assert_strcmp(metric->group_by[0].field, group_by_field); + } else { + test_assert(metric->group_by_count == 0); + test_assert(metric->group_by == NULL); + } + + test_assert(metric->group_value.type == value_type); +} + +#define DISCRETE_TEST_VAL_COUNT 3 +struct discrete_test { + const char *settings_blob; + unsigned int num_values; + const char *values_first[DISCRETE_TEST_VAL_COUNT]; + const char *values_second[DISCRETE_TEST_VAL_COUNT]; +}; + +static const struct discrete_test discrete_tests[] = { + { + "test_name sub_name", + 3, + { "eta", "kappa", "nu", }, + { "upsilon", "pi", "epsilon", }, + }, + { + "test_name:discrete sub_name:discrete", + 3, + { "apple", "bannana", "orange", }, + { "pie", "yoghurt", "cobbler", }, + }, + { + "test_name sub_name:discrete", + 3, + { "apollo", "gaia", "hermes", }, + { "thor", "odin", "loki", }, + }, +}; + +static void test_stats_metrics_group_by_discrete_real(const struct discrete_test *test) +{ + struct event *event; + unsigned int i, j; + + test_begin(t_strdup_printf("stats metrics (discrete group by) - %s", + test->settings_blob)); + + test_init(t_strdup_printf("metric=test\n" + "metric/test/metric_name=test\n" + "metric/test/filter=event=test\n" + "metric/test/group_by=%s\n" + "\n", test->settings_blob)); + + for (i = 0; i < test->num_values; i++) { + for (j = 0; j < test->num_values; j++) { + event = event_create(NULL); + event_add_category(event, &test_category); + event_set_name(event, "test"); + event_add_str(event, "test_name", test->values_first[i]); + event_add_str(event, "sub_name", test->values_second[j]); + test_event_send(event); + event_unref(&event); + } + } + + /* check total number of events */ + test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == test->num_values * test->num_values); + + /* analyze the structure */ + struct stats_metrics_iter *iter = stats_metrics_iterate_init(stats_metrics); + const struct metric *root_metric = stats_metrics_iterate(iter); + stats_metrics_iterate_deinit(&iter); + + test_stats_metrics_group_by_check_one(root_metric, + NULL, + test->num_values * test->num_values, + test->num_values, + 2, STATS_METRIC_GROUPBY_DISCRETE, + "test_name", 0); + + struct metric *const *first = array_idx(&root_metric->sub_metrics, 0); + + /* examime each sub-metric */ + for (i = 0; i < test->num_values; i++) { + test_stats_metrics_group_by_check_one(first[i], + test->values_first[i], + test->num_values, + test->num_values, + 1, STATS_METRIC_GROUPBY_DISCRETE, + "sub_name", + METRIC_VALUE_TYPE_STR); + + struct metric *const *second = array_idx(&first[i]->sub_metrics, 0); + + /* examine each sub-sub-metric */ + for (j = 0; j < test->num_values; j++) { + test_stats_metrics_group_by_check_one(second[j], + test->values_second[j], + 1, 0, 0, 0, NULL, + METRIC_VALUE_TYPE_STR); + } + } + + test_deinit(); + test_end(); +} + +static void test_stats_metrics_group_by_discrete(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(discrete_tests); i++) + test_stats_metrics_group_by_discrete_real(&discrete_tests[i]); +} + +#define QUANTIZED_TEST_VAL_COUNT 15 +struct quantized_test { + const char *settings_blob; + unsigned int num_inputs; + intmax_t input_vals[QUANTIZED_TEST_VAL_COUNT]; + + unsigned int num_sub_metrics; + + unsigned int num_ranges; + struct { + struct stats_metric_settings_bucket_range range; + intmax_t count; + } ranges[QUANTIZED_TEST_VAL_COUNT]; +}; + +static const struct quantized_test quantized_tests[] = { + { + "linear:100:1000:100", + 13, + { 0, 50, 100, 101, 200, 201, 250, 301, 900, 901, 1000, 1001, 2000 }, + 7, + 11, + { { { INTMAX_MIN, 100 }, 3 }, + { { 100, 200 }, 2 }, + { { 200, 300 }, 2 }, + { { 300, 400 }, 1 }, + { { 400, 500 }, 0 }, + { { 500, 600 }, 0 }, + { { 600, 700 }, 0 }, + { { 700, 800 }, 0 }, + { { 800, 900 }, 1 }, + { { 900, 1000 }, 2 }, + { { 1000, INTMAX_MAX }, 2 }, + } + }, + { + /* start at 0 */ + "exponential:0:6:10", + 12, + { 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 }, + 7, + 8, + { { { INTMAX_MIN, 1 }, 1 }, + { { 1, 10 }, 2 }, + { { 10, 100 }, 2 }, + { { 100, 1000 }, 3 }, + { { 1000, 10000 }, 1 }, + { { 10000, 100000 }, 0 }, + { { 100000, 1000000 }, 1 }, + { { 1000000, INTMAX_MAX }, 2 }, + } + }, + { + /* start at 0 */ + "exponential:0:6:2", + 9, + { 0, 1, 2, 4, 5, 20, 64, 65, 100 }, + 7, + 8, + { { { INTMAX_MIN, 1 }, 2 }, + { { 1, 2 }, 1 }, + { { 2, 4 }, 1 }, + { { 4, 8 }, 1 }, + { { 8, 16 }, 0 }, + { { 16, 32 }, 1 }, + { { 32, 64 }, 1 }, + { { 64, INTMAX_MAX }, 2 }, + } + }, + { + /* start at >0 */ + "exponential:2:6:10", + 12, + { 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 }, + 5, + 6, + { { { INTMAX_MIN, 100 }, 5 }, + { { 100, 1000 }, 3 }, + { { 1000, 10000 }, 1 }, + { { 10000, 100000 }, 0 }, + { { 100000, 1000000 }, 1 }, + { { 1000000, INTMAX_MAX }, 2 }, + } + }, + { + /* start at >0 */ + "exponential:2:6:2", + 9, + { 0, 1, 2, 4, 5, 20, 64, 65, 100 }, + 5, + 6, + { { { INTMAX_MIN, 4 }, 4 }, + { { 4, 8 }, 1 }, + { { 8, 16 }, 0 }, + { { 16, 32 }, 1 }, + { { 32, 64 }, 1 }, + { { 64, INTMAX_MAX }, 2 }, + } + }, +}; + +static void test_stats_metrics_group_by_quantized_real(const struct quantized_test *test) +{ + unsigned int i; + + test_begin(t_strdup_printf("stats metrics (quantized group by) - %s", + test->settings_blob)); + + test_init(t_strdup_printf("metric=test\n" + "metric/test/metric_name=test\n" + "metric/test/filter=event=test\n" + "metric/test/group_by=test_name foobar:%s\n" + "\n", test->settings_blob)); + + struct event *event; + + for (i = 0; i < test->num_inputs; i++) { + event = event_create(NULL); + event_add_category(event, &test_category); + event_set_name(event, "test"); + event_add_str(event, "test_name", "alpha"); + event_add_int(event, "foobar", test->input_vals[i]); + test_event_send(event); + event_unref(&event); + } + + /* check total number of events */ + test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == test->num_inputs); + + /* analyze the structure */ + struct stats_metrics_iter *iter = stats_metrics_iterate_init(stats_metrics); + const struct metric *root_metric = stats_metrics_iterate(iter); + stats_metrics_iterate_deinit(&iter); + + test_stats_metrics_group_by_check_one(root_metric, NULL, test->num_inputs, + 1, 2, STATS_METRIC_GROUPBY_DISCRETE, + "test_name", 0); + + /* examine first level sub-metric */ + struct metric *const *first = array_idx(&root_metric->sub_metrics, 0); + test_stats_metrics_group_by_check_one(first[0], + "alpha", + test->num_inputs, + test->num_sub_metrics, + 1, + STATS_METRIC_GROUPBY_QUANTIZED, + "foobar", + METRIC_VALUE_TYPE_STR); + + /* check the ranges */ + test_assert(first[0]->group_by[0].num_ranges == test->num_ranges); + for (i = 0; i < test->num_ranges; i++) { + test_assert(first[0]->group_by[0].ranges[i].min == test->ranges[i].range.min); + test_assert(first[0]->group_by[0].ranges[i].max == test->ranges[i].range.max); + } + + /* examine second level sub-metrics */ + struct metric *const *second = array_idx(&first[0]->sub_metrics, 0); + + for (i = 0; i < test->num_sub_metrics; i++) { + const char *sub_name; + intmax_t range_idx; + + /* we check these first, before we use the value below */ + test_assert(second[i]->group_value.type == METRIC_VALUE_TYPE_BUCKET_INDEX); + test_assert(second[i]->group_value.intmax < test->num_ranges); + + range_idx = second[i]->group_value.intmax; + + /* construct the expected sub-metric name */ + if (test->ranges[range_idx].range.min == INTMAX_MIN) { + sub_name = t_strdup_printf("foobar_ninf_%jd", + test->ranges[range_idx].range.max); + } else if (test->ranges[range_idx].range.max == INTMAX_MAX) { + sub_name = t_strdup_printf("foobar_%jd_inf", + test->ranges[range_idx].range.min + 1); + } else { + sub_name = t_strdup_printf("foobar_%jd_%jd", + test->ranges[range_idx].range.min + 1, + test->ranges[range_idx].range.max); + } + + test_stats_metrics_group_by_check_one(second[i], + sub_name, + test->ranges[second[i]->group_value.intmax].count, + 0, 0, 0, NULL, + METRIC_VALUE_TYPE_BUCKET_INDEX); + } + + test_deinit(); + test_end(); +} + +static void test_stats_metrics_group_by_quantized(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(quantized_tests); i++) + test_stats_metrics_group_by_quantized_real(&quantized_tests[i]); +} + +int main(void) { + void (*const test_functions[])(void) = { + test_stats_metrics, + test_stats_metrics_filter, + test_stats_metrics_group_by_discrete, + test_stats_metrics_group_by_quantized, + NULL + }; + + int ret = test_run(test_functions); + + return ret; +} |