1 /* Copyright (c) 2019 Dovecot authors, see the included COPYING file */
2
3 #include "test-stats-common.h"
4 #include "master-service-private.h"
5 #include "client-reader.h"
6 #include "connection.h"
7 #include "ostream.h"
8
9 static struct connection_list *conn_list;
10
11 struct test_connection {
12 struct connection conn;
13
14 unsigned int row_count;
15 };
16
test_reader_server_destroy(struct connection * conn)17 static void test_reader_server_destroy(struct connection *conn)
18 {
19 io_loop_stop(conn->ioloop);
20 }
21
22 static struct connection_settings client_set = {
23 .service_name_in = "stats-reader-server",
24 .service_name_out = "stats-reader-client",
25 .major_version = 2,
26 .minor_version = 0,
27 .allow_empty_args_input = TRUE,
28 .input_max_size = SIZE_MAX,
29 .output_max_size = SIZE_MAX,
30 .client = TRUE,
31 };
32
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)33 bool test_stats_callback(struct event *event,
34 enum event_callback_type type ATTR_UNUSED,
35 struct failure_context *ctx, const char *fmt ATTR_UNUSED,
36 va_list args ATTR_UNUSED)
37 {
38 if (stats_metrics != NULL) {
39 stats_metrics_event(stats_metrics, event, ctx);
40 struct event_filter *filter =
41 stats_metrics_get_event_filter(stats_metrics);
42 return !event_filter_match(filter, event, ctx);
43 }
44 return TRUE;
45 }
46
47 static const char *settings_blob_1 =
48 "metric=test\n"
49 "metric/test/metric_name=test\n"
50 "metric/test/filter=event=test\n"
51 "\n";
52
test_reader_server_input_args(struct connection * conn ATTR_UNUSED,const char * const * args)53 static int test_reader_server_input_args(struct connection *conn ATTR_UNUSED,
54 const char *const *args)
55 {
56 if (args[0] == NULL)
57 return -1;
58
59 test_assert_strcmp(args[0], "test");
60 test_assert_strcmp(args[1], "1");
61
62 return 1;
63 }
64
test_dump_metrics(void)65 static void test_dump_metrics(void)
66 {
67 int fds[2];
68
69 test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
70
71 struct connection *conn = i_new(struct connection, 1);
72
73 struct ioloop *loop = io_loop_create();
74
75 client_reader_create(fds[1]);
76 connection_init_client_fd(conn_list, conn, "stats", fds[0], fds[0]);
77 o_stream_nsend_str(conn->output, "DUMP\tcount\n");
78
79 io_loop_run(loop);
80 connection_deinit(conn);
81 i_free(conn);
82
83 /* allow client-reader to finish up */
84 io_loop_set_running(loop);
85 io_loop_handler_run(loop);
86
87 io_loop_destroy(&loop);
88 }
89
test_client_reader(void)90 static void test_client_reader(void)
91 {
92 const struct connection_vfuncs client_vfuncs = {
93 .input_args = test_reader_server_input_args,
94 .destroy = test_reader_server_destroy,
95 };
96
97 test_begin("client reader");
98
99 /* register some stats */
100 test_init(settings_blob_1);
101
102 client_readers_init();
103 conn_list = connection_list_init(&client_set, &client_vfuncs);
104
105 /* push event in */
106 struct event *event = event_create(NULL);
107 event_add_category(event, &test_category);
108 event_set_name(event, "test");
109 test_event_send(event);
110 event_unref(&event);
111
112 test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == 1);
113 test_assert(get_stats_dist_field("test", STATS_DIST_SUM) > 0);
114
115 /* check output from reader */
116 test_dump_metrics();
117
118 test_deinit();
119
120 client_readers_deinit();
121 connection_list_deinit(&conn_list);
122
123 test_end();
124 }
125
126 static const char *settings_blob_2 =
127 "metric=test\n"
128 "metric/test/metric_name=test\n"
129 "metric/test/filter=event=test\n"
130 "metric/test/group_by=test_name\n"
131 "\n";
132
133 static int
test_reader_server_input_args_group_by(struct connection * conn,const char * const * args)134 test_reader_server_input_args_group_by(struct connection *conn,
135 const char *const *args)
136 {
137 struct test_connection *tconn =
138 container_of(conn, struct test_connection, conn);
139
140 if (args[0] == NULL)
141 return -1;
142
143 tconn->row_count++;
144
145 if (tconn->row_count == 1) {
146 test_assert_strcmp(args[0], "test");
147 test_assert_strcmp(args[1], "1");
148 } else if (tconn->row_count == 2) {
149 test_assert_strcmp(args[0], "test_alpha");
150 test_assert_strcmp(args[1], "1");
151 }
152 return 1;
153 }
154
test_dump_metrics_group_by(void)155 static void test_dump_metrics_group_by(void)
156 {
157 int fds[2];
158
159 test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
160
161 struct test_connection *conn = i_new(struct test_connection, 1);
162
163 struct ioloop *loop = io_loop_create();
164
165 client_reader_create(fds[1]);
166 connection_init_client_fd(conn_list, &conn->conn, "stats", fds[0], fds[0]);
167 o_stream_nsend_str(conn->conn.output, "DUMP\tcount\n");
168
169 io_loop_run(loop);
170 connection_deinit(&conn->conn);
171 i_free(conn);
172
173 io_loop_set_running(loop);
174 io_loop_handler_run(loop);
175
176 io_loop_destroy(&loop);
177 }
178
test_client_reader_group_by(void)179 static void test_client_reader_group_by(void)
180 {
181 const struct connection_vfuncs client_vfuncs = {
182 .input_args = test_reader_server_input_args_group_by,
183 .destroy = test_reader_server_destroy,
184 };
185
186 test_begin("client reader (group by)");
187
188 /* register some stats */
189 test_init(settings_blob_2);
190
191 client_readers_init();
192 conn_list = connection_list_init(&client_set, &client_vfuncs);
193
194 /* push event in */
195 struct event *event = event_create(NULL);
196 event_add_category(event, &test_category);
197 event_set_name(event, "test");
198 event_add_str(event, "event_name", "alpha");
199 test_event_send(event);
200 event_unref(&event);
201
202 test_assert(get_stats_dist_field("test", STATS_DIST_COUNT) == 1);
203 test_assert(get_stats_dist_field("test", STATS_DIST_SUM) > 0);
204
205 /* check output from reader */
206 test_dump_metrics_group_by();
207
208 test_deinit();
209
210 client_readers_deinit();
211 connection_list_deinit(&conn_list);
212
213 test_end();
214 }
215
main(void)216 int main(void) {
217 /* fake master service to pretend destroying
218 connections. */
219 struct master_service local_master_service = {
220 .stopping = TRUE,
221 .total_available_count = 100,
222 .service_count_left = 100,
223 };
224 void (*const test_functions[])(void) = {
225 test_client_reader,
226 test_client_reader_group_by,
227 NULL
228 };
229
230 master_service = &local_master_service;
231
232 int ret = test_run(test_functions);
233
234 return ret;
235 }
236