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