doveadm stats add --group-by preserves the old syntax for now.
reader_client_input_metrics_add(struct reader_client *client,
const char *const *args)
{
+ ARRAY_TYPE(stats_metric_settings_group_by) group_by;
const char *error;
if (str_array_length(args) < 7) {
set->pool = pool;
set->name = p_strdup(pool, args[0]);
set->description = p_strdup(pool, args[1]);
- set->group_by = p_strdup(pool, args[3]);
set->filter = p_strdup(pool, args[4]);
set->exporter = p_strdup(pool, args[5]);
+ if (!parse_legacy_metric_group_by(pool, args[3], &group_by, &error)) {
+ e_error(client->conn.event,
+ "METRICS-ADD: Invalid metric_group_by: %s", error);
+ pool_unref(&pool);
+ return -1;
+ }
+
p_array_init(&set->fields, pool, 4);
if (settings_parse_boollist_string(args[2], pool, &set->fields,
&error) < 0) {
}
o_stream_cork(client->conn.output);
- if (stats_metrics_add_dynamic(stats_metrics, set, &error)) {
+ if (stats_metrics_add_dynamic(stats_metrics, set, &group_by, &error)) {
client_writer_update_connections();
o_stream_nsend(client->conn.output, "+", 1);
} else {
ret = -1;
} else {
struct event *event = event_create(metrics->event);
- event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME,
- p_strdup_printf(event_get_pool(event),
- "event_exporter/%s", filter_name));
+ event_add_str(event, "event_exporter", filter_name);
ret = stats_exporters_add_set(metrics, event, set, error_r);
event_unref(&event);
}
static int stats_metrics_add_set(struct stats_metrics *metrics,
const struct stats_metric_settings *set,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by,
const char **error_r)
{
struct event_exporter *exporter = NULL;
fields = settings_boollist_get(&set->fields);
metric = stats_metric_alloc(metrics->pool, set->name, set, fields);
- if (array_is_created(&set->parsed_group_by))
- metric->group_by = array_get(&set->parsed_group_by,
- &metric->group_by_count);
+ if (array_is_created(group_by))
+ metric->group_by = array_get(group_by, &metric->group_by_count);
array_push_back(&metrics->metrics, &metric);
return 0;
}
+static bool
+stats_metrics_group_by_exponential_check(const struct stats_metric_group_by_method_settings *set,
+ const char **error_r)
+{
+ if (set->exponential_base != 2 && set->exponential_base != 10) {
+ *error_r = "metric_group_by_method_exponential_base must be 2 or 10";
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool
+stats_metrics_group_by_linear_check(const struct stats_metric_group_by_method_settings *set,
+ const char **error_r)
+{
+ if (set->linear_step == 0) {
+ *error_r = "metric_group_by_method_linear_step must not be 0";
+ return FALSE;
+ }
+ if (set->linear_min >= set->linear_max) {
+ *error_r = t_strdup_printf(
+ "metric_group_by_method_linear_min (%ju) must be smaller than "
+ "metric_group_by_method_linear_max (%ju)",
+ set->linear_min, set->linear_max);
+ return FALSE;
+ }
+ if ((set->linear_min + set->linear_step) > set->linear_max) {
+ *error_r = t_strdup_printf(
+ "metric_group_by_method_linear_min (%ju) + "
+ "metric_group_by_method_linear_step (%ju) must be <= "
+ "metric_group_by_method_linear_max (%ju)",
+ set->linear_min, set->linear_step, set->linear_max);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int
+stats_metrics_get_group_by_method(struct event *event, pool_t pool,
+ struct stats_metric_settings_group_by *group_by,
+ const char **error_r)
+{
+ const struct stats_metric_group_by_method_settings *set;
+
+ if (settings_get(event, &stats_metric_group_by_method_setting_parser_info,
+ 0, &set, error_r) < 0)
+ return -1;
+
+ if (strcmp(set->method, "discrete") == 0) {
+ group_by->func = STATS_METRIC_GROUPBY_DISCRETE;
+ group_by->discrete_modifier =
+ p_strdup_empty(pool, set->discrete_modifier);
+ } else if (strcmp(set->method, "exponential") == 0) {
+ if (!stats_metrics_group_by_exponential_check(set, error_r))
+ return -1;
+ metrics_group_by_exponential_init(group_by, pool,
+ set->exponential_base,
+ set->exponential_min_magnitude,
+ set->exponential_max_magnitude);
+ } else if (strcmp(set->method, "linear") == 0) {
+ if (!stats_metrics_group_by_linear_check(set, error_r))
+ return -1;
+ metrics_group_by_linear_init(group_by, pool,
+ set->linear_min, set->linear_max, set->linear_step);
+ } else {
+ i_unreached();
+ }
+
+ settings_free(set);
+ return 0;
+}
+
+static int
+stats_metrics_get_group_by(struct event *event,
+ const struct stats_metric_settings *set,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by_r,
+ const char **error_r)
+{
+ const struct stats_metric_group_by_settings *group_by_set;
+ const char *group_by_name;
+
+ if (array_is_empty(&set->group_by)) {
+ i_zero(group_by_r);
+ return 0;
+ }
+ p_array_init(group_by_r, set->pool, array_count(&set->group_by));
+ array_foreach_elem(&set->group_by, group_by_name) {
+ if (settings_get_filter(event,
+ "metric_group_by", group_by_name,
+ &stats_metric_group_by_setting_parser_info,
+ 0, &group_by_set, error_r) < 0)
+ return -1;
+
+ struct stats_metric_settings_group_by *group_by =
+ array_append_space(group_by_r);
+ group_by->field = p_strdup(set->pool, group_by_set->field);
+
+ int ret = 0;
+ if (array_is_empty(&group_by_set->method)) {
+ /* default to discrete */
+ group_by->func = STATS_METRIC_GROUPBY_DISCRETE;
+ } else if (array_count(&group_by_set->method) > 1) {
+ *error_r = "Only one metric_group_by_method named filter is allowed";
+ ret = -1;
+ } else {
+ struct event *group_event = event_create(event);
+ event_add_str(group_event, "metric_group_by",
+ group_by_name);
+ struct event *method_event = event_create(group_event);
+ event_add_str(group_event, "metric_group_by_method",
+ array_idx_elem(&group_by_set->method, 0));
+ ret = stats_metrics_get_group_by_method(method_event, set->pool,
+ group_by, error_r);
+ event_unref(&method_event);
+ event_unref(&group_event);
+ }
+ settings_free(group_by_set);
+ if (ret < 0) {
+ *error_r = t_strdup_printf("metric_group_by %s: %s",
+ group_by_name, *error_r);
+ return -1;
+ }
+ }
+ return 0;
+}
+
static int stats_metrics_add_filter(struct stats_metrics *metrics,
const char *filter_name,
const char **error_r)
{
- struct stats_metric_settings *set;
+ const struct stats_metric_settings *set;
int ret = 0;
if (settings_get_filter(metrics->event, "metric", filter_name,
*error_r = "Metric name can't be empty";
ret = -1;
} else {
- ret = stats_metrics_add_set(metrics, set, error_r);
+ ARRAY_TYPE(stats_metric_settings_group_by) group_by;
+ struct event *event = event_create(metrics->event);
+ event_add_str(event, "metric", filter_name);
+ ret = stats_metrics_get_group_by(event, set, &group_by, error_r);
+ if (ret == 0)
+ ret = stats_metrics_add_set(metrics, set, &group_by, error_r);
+ event_unref(&event);
}
settings_free(set);
return ret;
bool stats_metrics_add_dynamic(struct stats_metrics *metrics,
const struct stats_metric_settings *set,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by,
const char **error_r)
{
unsigned int existing_idx ATTR_UNUSED;
return FALSE;
}
- if (stats_metrics_add_set(metrics, set, error_r) < 0)
+ if (stats_metrics_add_set(metrics, set, group_by, error_r) < 0)
return FALSE;
return TRUE;
}
bool stats_metrics_add_dynamic(struct stats_metrics *metrics,
const struct stats_metric_settings *set,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by,
const char **error_r);
bool stats_metrics_remove_dynamic(struct stats_metrics *metrics,
.check_func = stats_exporter_settings_check,
};
+/*
+ * metric_group_by { } block settings
+ */
+
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type("metric_group_by_"#name, name, struct stats_metric_group_by_settings)
+
+static const struct setting_define stats_metric_group_by_setting_defines[] = {
+ DEF(STR, field),
+
+ { .type = SET_FILTER_ARRAY, .key = "metric_group_by_method",
+ .offset = offsetof(struct stats_metric_group_by_settings, method),
+ .filter_array_field_name = "metric_group_by_method_method", },
+
+ SETTING_DEFINE_LIST_END
+};
+
+static const struct stats_metric_group_by_settings stats_metric_group_by_default_settings = {
+ .field = "",
+ .method = ARRAY_INIT,
+};
+
+const struct setting_parser_info stats_metric_group_by_setting_parser_info = {
+ .name = "stats_metric_group_by",
+
+ .defines = stats_metric_group_by_setting_defines,
+ .defaults = &stats_metric_group_by_default_settings,
+
+ .struct_size = sizeof(struct stats_metric_group_by_settings),
+ .pool_offset1 = 1 + offsetof(struct stats_metric_group_by_settings, pool),
+};
+
+/*
+ * metric_group_by_method { } block settings
+ */
+
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type("metric_group_by_method_"#name, name, struct stats_metric_group_by_method_settings)
+
+static const struct setting_define stats_metric_group_by_method_setting_defines[] = {
+ DEF(ENUM, method),
+ DEF(STR_NOVARS, discrete_modifier),
+ DEF(UINT, exponential_min_magnitude),
+ DEF(UINT, exponential_max_magnitude),
+ DEF(UINT, exponential_base),
+ DEF(UINTMAX, linear_min),
+ DEF(UINTMAX, linear_max),
+ DEF(UINTMAX, linear_step),
+
+ SETTING_DEFINE_LIST_END
+};
+
+static const struct stats_metric_group_by_method_settings stats_metric_group_by_method_default_settings = {
+ .method = "discrete:exponential:linear",
+ .discrete_modifier = "",
+ .exponential_min_magnitude = 0,
+ .exponential_max_magnitude = 0,
+ .exponential_base = 10,
+ .linear_min = 0,
+ .linear_max = 0,
+ .linear_step = 0,
+};
+
+const struct setting_parser_info stats_metric_group_by_method_setting_parser_info = {
+ .name = "stats_metric_group_by_",
+
+ .defines = stats_metric_group_by_method_setting_defines,
+ .defaults = &stats_metric_group_by_method_default_settings,
+
+ .struct_size = sizeof(struct stats_metric_group_by_method_settings),
+ .pool_offset1 = 1 + offsetof(struct stats_metric_group_by_method_settings, pool),
+};
+
/*
* metric { } block settings
*/
static const struct setting_define stats_metric_setting_defines[] = {
DEF(STR, name),
DEF(BOOLLIST, fields),
- DEF(STR_NOVARS, group_by),
DEF(STR, filter),
DEF(STR, exporter),
DEF(BOOLLIST, exporter_include),
DEF(STR, description),
+
+ { .type = SET_FILTER_ARRAY, .key = "metric_group_by",
+ .offset = offsetof(struct stats_metric_settings, group_by),
+ .filter_array_field_name = "metric_group_by_field", },
+
SETTING_DEFINE_LIST_END
};
.fields = ARRAY_INIT,
.filter = "",
.exporter = "",
- .group_by = "",
+ .group_by = ARRAY_INIT,
.description = "",
};
return TRUE;
}
-static void
-metrics_group_by_exponential_init(struct stats_metric_settings_group_by *group_by,
- pool_t pool, unsigned int base,
- unsigned int min, unsigned int max)
+#ifdef CONFIG_BINARY
+void metrics_group_by_exponential_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, unsigned int base,
+ unsigned int min, unsigned int max);
+void metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, uint64_t min, uint64_t max,
+ uint64_t step);
+#endif
+
+void metrics_group_by_exponential_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, unsigned int base,
+ unsigned int min, unsigned int max)
{
group_by->func = STATS_METRIC_GROUPBY_QUANTIZED;
/*
}
}
-static void
-metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_by,
- pool_t pool, uint64_t min, uint64_t max,
- uint64_t step)
+void metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, uint64_t min, uint64_t max,
+ uint64_t step)
{
group_by->func = STATS_METRIC_GROUPBY_QUANTIZED;
/*
* The second bucket begins at 'min + 1', the third bucket begins at
* 'min + 1 * step + 1', the fourth at 'min + 2 * step + 1', and so on.
*/
+ i_assert(step > 0);
group_by->num_ranges = (max - min) / step + 2;
group_by->ranges = p_new(pool, struct stats_metric_settings_bucket_range,
group_by->num_ranges);
group_by->ranges[i].max = min + i * step;
}
}
+/* </settings checks> */
static bool parse_metric_group_by_common(const char *func,
const char *const *params,
return TRUE;
}
-static bool parse_metric_group_by(struct stats_metric_settings *set,
- pool_t pool, const char **error_r)
+bool parse_legacy_metric_group_by(pool_t pool, const char *group_by_str,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by_r,
+ const char **error_r)
{
- const char *const *tmp = t_strsplit_spaces(set->group_by, " ");
+ const char *const *tmp = t_strsplit_spaces(group_by_str, " ");
+ i_zero(group_by_r);
if (tmp[0] == NULL)
return TRUE;
- p_array_init(&set->parsed_group_by, pool, str_array_length(tmp));
+ p_array_init(group_by_r, pool, str_array_length(tmp));
/* For each group_by field */
for (; *tmp != NULL; tmp++) {
return FALSE;
}
- array_push_back(&set->parsed_group_by, &group_by);
+ array_push_back(group_by_r, &group_by);
}
return TRUE;
}
+/* <settings checks> */
static bool stats_metric_settings_check(void *_set, pool_t pool, const char **error_r)
{
struct stats_metric_settings *set = _set;
if (event_filter_parse(set->filter, set->parsed_filter, error_r) < 0)
return FALSE;
- if (!parse_metric_group_by(set, pool, error_r))
- return FALSE;
-
return TRUE;
}
enum event_exporter_time_fmt parsed_time_format;
};
+struct stats_metric_group_by_settings {
+ pool_t pool;
+ const char *field;
+ ARRAY_TYPE(const_string) method;
+};
+
+struct stats_metric_group_by_method_settings {
+ pool_t pool;
+
+ const char *method;
+
+ const char *discrete_modifier;
+
+ unsigned int exponential_min_magnitude;
+ unsigned int exponential_max_magnitude;
+ unsigned int exponential_base;
+
+ uintmax_t linear_min;
+ uintmax_t linear_max;
+ uintmax_t linear_step;
+};
+
/* <settings checks> */
enum stats_metric_group_by_func {
STATS_METRIC_GROUPBY_DISCRETE = 0,
unsigned int num_ranges;
struct stats_metric_settings_bucket_range *ranges;
};
+ARRAY_DEFINE_TYPE(stats_metric_settings_group_by,
+ struct stats_metric_settings_group_by);
/* </settings checks> */
struct stats_metric_settings {
const char *name;
const char *description;
ARRAY_TYPE(const_string) fields;
- const char *group_by;
+ ARRAY_TYPE(const_string) group_by;
const char *filter;
- ARRAY(struct stats_metric_settings_group_by) parsed_group_by;
struct event_filter *parsed_filter;
/* exporter related fields */
extern const struct setting_parser_info stats_setting_parser_info;
extern const struct setting_parser_info stats_metric_setting_parser_info;
+extern const struct setting_parser_info stats_metric_group_by_setting_parser_info;
+extern const struct setting_parser_info stats_metric_group_by_method_setting_parser_info;
extern const struct setting_parser_info stats_exporter_setting_parser_info;
extern const struct stats_metric_settings stats_metric_default_settings;
+bool parse_legacy_metric_group_by(pool_t pool, const char *group_by_str,
+ ARRAY_TYPE(stats_metric_settings_group_by) *group_by_r,
+ const char **error_r);
+void metrics_group_by_exponential_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, unsigned int base,
+ unsigned int min, unsigned int max);
+void metrics_group_by_linear_init(struct stats_metric_settings_group_by *group_by,
+ pool_t pool, uint64_t min, uint64_t max,
+ uint64_t step);
+
#endif
"metric=test",
"metric/test/metric_name=test",
"metric/test/filter=event=test",
- "metric/test/group_by=test_name",
+ "metric/test/metric_group_by=test_name",
+ "metric/test/metric_group_by/test_name/metric_group_by_field=test_name",
+ "metric/test/metric_group_by/test_name/metric_group_by_method=foo",
+ "metric/test/metric_group_by/test_name/metric_group_by_method/foo/method=linear",
+ "metric/test/metric_group_by/test_name/metric_group_by_method/foo/metric_group_by_method_linear_max=100",
+ "metric/test/metric_group_by/test_name/metric_group_by_method/foo/metric_group_by_method_linear_step=10",
NULL
};
#define DISCRETE_TEST_VAL_COUNT 3
struct discrete_test {
- const char *settings_blob;
+ const char *const *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",
+ (const char *const []){
+ "metric/test/group_by=test_name,sub_name",
+ "metric/test/group_by/test_name/field=test_name",
+ "metric/test/group_by/sub_name/field=sub_name",
+ NULL },
3,
{ "eta", "kappa", "nu", },
{ "upsilon", "pi", "epsilon", },
},
{
- "test_name:discrete sub_name:discrete",
+ (const char *const []){
+ "metric/test/group_by=test_name,sub_name",
+ "metric/test/group_by/test_name/field=test_name",
+ "metric/test/group_by/test_name/method=discrete",
+ "metric/test/group_by/test_name/method/discrete/method=discrete",
+ "metric/test/group_by/sub_name/field=sub_name",
+ "metric/test/group_by/sub_name/method=discrete",
+ "metric/test/group_by/sub_name/method/discrete/method=discrete",
+ NULL },
3,
{ "apple", "bannana", "orange", },
{ "pie", "yoghurt", "cobbler", },
},
{
- "test_name sub_name:discrete",
+ (const char *const []){
+ "metric/test/group_by=test_name,sub_name",
+ "metric/test/group_by/test_name/field=test_name",
+ "metric/test/group_by/test_name/method/discrete/method=discrete",
+ "metric/test/group_by/sub_name/field=sub_name",
+ "metric/test/group_by/sub_name/method=discrete",
+ "metric/test/group_by/sub_name/method/discrete/method=discrete",
+ NULL },
3,
{ "apollo", "gaia", "hermes", },
{ "thor", "odin", "loki", },
},
};
-static void test_stats_metrics_group_by_discrete_real(const struct discrete_test *test)
+static void
+test_stats_metrics_group_by_discrete_real(const struct discrete_test *test,
+ unsigned int group_idx)
{
struct event *event;
unsigned int i, j;
- test_begin(t_strdup_printf("stats metrics (discrete group by) - %s",
- test->settings_blob));
+ test_begin(t_strdup_printf("stats metrics (discrete group by) - %u",
+ group_idx));
- const char *const settings[] = {
+ const char *const base_settings[] = {
"metric=test",
"metric/test/metric_name=test",
"metric/test/filter=event=test",
- t_strdup_printf("metric/test/group_by=%s", test->settings_blob),
NULL
};
- test_init(settings);
+ const char *const *all_settings = t_strsplit(
+ t_strconcat(t_strarray_join(base_settings, " "), " ",
+ t_strarray_join(test->settings_blob, " "), NULL), " ");
+ test_init(all_settings);
for (i = 0; i < test->num_values; i++) {
for (j = 0; j < test->num_values; j++) {
unsigned int i;
for (i = 0; i < N_ELEMENTS(discrete_tests); i++)
- test_stats_metrics_group_by_discrete_real(&discrete_tests[i]);
+ test_stats_metrics_group_by_discrete_real(&discrete_tests[i], i);
}
#define QUANTIZED_TEST_VAL_COUNT 15
struct quantized_test {
- const char *settings_blob;
+ const char *const *settings_blob;
unsigned int num_inputs;
intmax_t input_vals[QUANTIZED_TEST_VAL_COUNT];
static const struct quantized_test quantized_tests[] = {
{
- "linear:100:1000:100",
+ (const char *const []){
+ "metric/test/group_by/foobar/method=linear",
+ "metric/test/group_by/foobar/method/linear/method=linear",
+ "metric/test/group_by/foobar/method/linear/min=100",
+ "metric/test/group_by/foobar/method/linear/max=1000",
+ "metric/test/group_by/foobar/method/linear/step=100",
+ NULL },
13,
{ 0, 50, 100, 101, 200, 201, 250, 301, 900, 901, 1000, 1001, 2000 },
7,
},
{
/* start at 0 */
- "exponential:0:6:10",
+ (const char *const []){
+ "metric/test/group_by/foobar/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/max_magnitude=6",
+ NULL },
12,
{ 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 },
7,
},
{
/* start at 0 */
- "exponential:0:6:2",
+ (const char *const []){
+ "metric/test/group_by/foobar/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/max_magnitude=6",
+ "metric/test/group_by/foobar/method/exponential/base=2",
+ NULL },
9,
{ 0, 1, 2, 4, 5, 20, 64, 65, 100 },
7,
},
{
/* start at >0 */
- "exponential:2:6:10",
+ (const char *const []){
+ "metric/test/group_by/foobar/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/min_magnitude=2",
+ "metric/test/group_by/foobar/method/exponential/max_magnitude=6",
+ NULL },
12,
{ 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 },
5,
},
{
/* start at >0 */
- "exponential:2:6:2",
+ (const char *const []){
+ "metric/test/group_by/foobar/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/method=exponential",
+ "metric/test/group_by/foobar/method/exponential/min_magnitude=2",
+ "metric/test/group_by/foobar/method/exponential/max_magnitude=6",
+ "metric/test/group_by/foobar/method/exponential/base=2",
+ NULL },
9,
{ 0, 1, 2, 4, 5, 20, 64, 65, 100 },
5,
},
};
-static void test_stats_metrics_group_by_quantized_real(const struct quantized_test *test)
+static void
+test_stats_metrics_group_by_quantized_real(const struct quantized_test *test,
+ unsigned int group_idx)
{
unsigned int i;
- test_begin(t_strdup_printf("stats metrics (quantized group by) - %s",
- test->settings_blob));
+ test_begin(t_strdup_printf("stats metrics (quantized group by) - %u",
+ group_idx));
- const char *const settings[] = {
+ const char *const base_settings[] = {
"metric=test",
"metric/test/metric_name=test",
"metric/test/filter=event=test",
- t_strdup_printf("metric/test/group_by=test_name foobar:%s",
- test->settings_blob),
+ "metric/test/group_by=test_name,foobar",
+ "metric/test/group_by/test_name/field=test_name",
+ "metric/test/group_by/foobar/field=foobar",
NULL
};
- test_init(settings);
+ const char *const *all_settings = t_strsplit(
+ t_strconcat(t_strarray_join(base_settings, " "), " ",
+ t_strarray_join(test->settings_blob, " "), NULL), " ");
+ test_init(all_settings);
struct event *event;
unsigned int i;
for (i = 0; i < N_ELEMENTS(quantized_tests); i++)
- test_stats_metrics_group_by_quantized_real(&quantized_tests[i]);
+ test_stats_metrics_group_by_quantized_real(&quantized_tests[i], i);
}
int main(void) {