changeset 20359:bd15bf4e46f5

lib-http: test-http-payload: Added tests for the use of nested ioloops.
author Stephan Bosch <stephan@dovecot.fi>
date Tue, 24 May 2016 03:11:07 +0200
parents 075b812a6e6e
children 2ce144c4dbd7
files src/lib-http/test-http-payload.c
diffstat 1 files changed, 142 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-http/test-http-payload.c	Mon May 23 21:57:18 2016 +0200
+++ b/src/lib-http/test-http-payload.c	Tue May 24 03:11:07 2016 +0200
@@ -31,11 +31,15 @@
 static size_t read_server_partial = 0;
 static size_t read_client_partial = 0;
 static unsigned int test_max_pending = 200;
+static unsigned int client_ioloop_nesting = 0;
 
 static struct ip_addr bind_ip;
 static in_port_t bind_port = 0;
 static int fd_listen = -1;
 static pid_t server_pid = (pid_t)-1;
+static struct ioloop *ioloop_nested = NULL;
+static unsigned ioloop_nested_first = 0;
+static unsigned ioloop_nested_last = 0;
 
 /*
  * Test files
@@ -583,6 +587,9 @@
  */
 
 struct test_client_request {
+	struct test_client_request *prev, *next;
+	struct http_client_request *hreq;
+
 	struct io *io;
 	struct istream *payload;
 	struct istream *file;
@@ -590,8 +597,20 @@
 };
 
 static struct http_client *http_client;
+static struct test_client_request *client_requests;
 static unsigned int client_files_first, client_files_last;
 
+static struct test_client_request *
+test_client_request_new(void)
+{
+	struct test_client_request *tcreq;
+
+	tcreq = i_new(struct test_client_request, 1);
+	DLLIST_PREPEND(&client_requests, tcreq);
+
+	return tcreq;
+}
+
 static void
 test_client_request_destroy(void *context)
 {
@@ -604,25 +623,41 @@
 		i_stream_unref(&tcreq->payload);
 	if (tcreq->file != NULL)
 		i_stream_unref(&tcreq->file);
+
+	DLLIST_REMOVE(&client_requests, tcreq);
 	i_free(tcreq);
 }
 
+static void
+test_client_requests_switch_ioloop(void)
+{
+	struct test_client_request *tcreq;
+
+	for (tcreq = client_requests; tcreq != NULL;
+		tcreq = tcreq->next) {
+		if (tcreq->io != NULL)
+			tcreq->io = io_loop_move_io(&tcreq->io);
+		if (tcreq->payload != NULL)
+			i_stream_switch_ioloop(tcreq->payload);
+	}
+}
+
 /* download */
 
 static void test_client_download_continue(void);
 
 static void
-test_client_download_finished(struct test_client_request *tcreq)
+test_client_download_finished(unsigned int files_idx)
 {
 	const char **paths;
 	unsigned int count;
 
 	paths = array_get_modifiable(&files, &count);
-	i_assert(tcreq->files_idx < count);
+	i_assert(files_idx < count);
 	i_assert(client_files_first < count);
-	i_assert(paths[tcreq->files_idx] != NULL);
+	i_assert(paths[files_idx] != NULL);
 
-	paths[tcreq->files_idx] = NULL;
+	paths[files_idx] = NULL;
 	test_client_download_continue();
 }
 
@@ -632,6 +667,7 @@
 	struct istream *payload = tcreq->payload;
 	const unsigned char *pdata, *fdata;
 	size_t psize, fsize, pleft;
+	unsigned int files_idx = tcreq->files_idx;
 	off_t ret;
 
 	/* read payload */
@@ -689,13 +725,13 @@
 				tcreq->files_idx);
 		}
 
-		/* finished */
-		test_client_download_finished(tcreq);
-
 		/* dereference payload stream; finishes the request */
 		tcreq->payload = NULL;
 		io_remove(&tcreq->io); /* holds a reference too */
 		i_stream_unref(&payload);
+
+		/* finished */
+		test_client_download_finished(files_idx);
 	}
 }
 
@@ -741,7 +777,7 @@
 				path, resp->status, resp->reason);
 		}
 		i_stream_unref(&fstream);
-		test_client_download_finished(tcreq);
+		test_client_download_finished(tcreq->files_idx);
 		return;
 	}
 
@@ -752,7 +788,7 @@
 				path, tcreq->files_idx);
 		}
 		i_stream_unref(&fstream);
-		test_client_download_finished(tcreq);
+		test_client_download_finished(tcreq->files_idx);
 		return;
 	}
 
@@ -806,7 +842,7 @@
 		client_files_last++) {
 		const char *path = paths[client_files_last];
 
-		tcreq = i_new(struct test_client_request, 1);
+		tcreq = test_client_request_new();
 		tcreq->files_idx = client_files_last;
 
 		if (debug) {
@@ -814,7 +850,7 @@
 				"retrieving %s [%u]",
 				path, tcreq->files_idx);
 		}
-		hreq = http_client_request(http_client,
+		hreq = tcreq->hreq = http_client_request(http_client,
 			"GET", net_ip2addr(&bind_ip),
 			t_strconcat("/download/", path, NULL),
 			test_client_download_response, tcreq);
@@ -841,17 +877,17 @@
 static void test_client_echo_continue(void);
 
 static void
-test_client_echo_finished(struct test_client_request *tcreq)
+test_client_echo_finished(unsigned int files_idx)
 {
 	const char **paths;
 	unsigned int count;
 
 	paths = array_get_modifiable(&files, &count);
-	i_assert(tcreq->files_idx < count);
+	i_assert(files_idx < count);
 	i_assert(client_files_first < count);
-	i_assert(paths[tcreq->files_idx] != NULL);
+	i_assert(paths[files_idx] != NULL);
 
-	paths[tcreq->files_idx] = NULL;
+	paths[files_idx] = NULL;
 	test_client_echo_continue();
 }
 
@@ -861,6 +897,7 @@
 	struct istream *payload = tcreq->payload;
 	const unsigned char *pdata, *fdata;
 	size_t psize, fsize, pleft;
+	unsigned int files_idx = tcreq->files_idx;
 	off_t ret;
 
 	/* read payload */
@@ -918,13 +955,13 @@
 				tcreq->files_idx);
 		}
 
-		/* finished */
-		test_client_echo_finished(tcreq);
-
 		/* dereference payload stream; finishes the request */
 		tcreq->payload = NULL;
 		io_remove(&tcreq->io); /* holds a reference too */
 		i_stream_unref(&payload);
+
+		/* finished */
+		test_client_echo_finished(files_idx);
 	}
 }
 
@@ -982,7 +1019,7 @@
 				path, tcreq->files_idx);
 		}
 		i_stream_unref(&fstream);
-		test_client_echo_finished(tcreq);
+		test_client_echo_finished(tcreq->files_idx);
 		return;
 	}
 
@@ -1001,7 +1038,7 @@
 	struct test_client_request *tcreq;
 	struct http_client_request *hreq;
 	const char **paths;
-	unsigned int count;
+	unsigned int count, first_submitted;
 
 	paths = array_get_modifiable(&files, &count);
 
@@ -1010,7 +1047,7 @@
 
 	i_assert(client_files_first <= client_files_last);
 	for (; client_files_first < client_files_last &&
-		paths[client_files_first] == NULL; client_files_first++)
+		paths[client_files_first] == NULL; client_files_first++);
 
 	if (debug) {
 		i_debug("test client: echo: "
@@ -1020,7 +1057,9 @@
 	if (debug && client_files_first < count) {
 		const char *path = paths[client_files_first];
 		i_debug("test client: echo: "
-			"next blocking: %s", (path == NULL ? "none" : path));
+			"next blocking: %s [%d]",
+			(path == NULL ? "none" : path),
+			client_files_first);
 	}
 
 	if (client_files_first >= count) {
@@ -1028,6 +1067,7 @@
 		return;
 	}
 
+	first_submitted = client_files_last;
 	for (; client_files_last < count &&
 			(client_files_last - client_files_first) < test_max_pending;
 		client_files_last++) {
@@ -1051,10 +1091,10 @@
 				path, client_files_last);
 		}
 
-		tcreq = i_new(struct test_client_request, 1);
+		tcreq = test_client_request_new();
 		tcreq->files_idx = client_files_last;
 
-		hreq = http_client_request(http_client,
+		hreq = tcreq->hreq = http_client_request(http_client,
 			"PUT", net_ip2addr(&bind_ip),
 			t_strconcat("/echo/", path, NULL),
 			test_client_echo_response, tcreq);
@@ -1067,6 +1107,60 @@
 
 		i_stream_unref(&fstream);
 	}
+
+	/* run nested ioloop (if requested) if new requests cross a nesting
+	   boundary */
+	if (ioloop_nested != NULL) {
+		unsigned int i;
+
+		i_assert(ioloop_nested_first <= count);
+		i_assert(ioloop_nested_last <= count);
+		for (i = ioloop_nested_first; i < ioloop_nested_last; i++) {
+			if (paths[i] != NULL) {
+				if (debug) {
+					i_debug("test client: "
+						"not leaving ioloop [%u]", i);
+				}
+				break;
+			}
+		}
+
+		if (i == ioloop_nested_last)
+			io_loop_stop(ioloop_nested);
+	} else if (client_ioloop_nesting > 0 &&
+		((client_files_last / client_ioloop_nesting) !=
+			(first_submitted / client_ioloop_nesting)) ) {
+		struct ioloop *prev_ioloop = current_ioloop;
+
+		ioloop_nested_first = first_submitted;
+		ioloop_nested_last = first_submitted + client_ioloop_nesting;
+		if (ioloop_nested_last > client_files_last)
+			ioloop_nested_last = client_files_last;
+
+		if (debug) {
+			i_debug("test client: echo: entering ioloop for %u...%u",
+				ioloop_nested_first, ioloop_nested_last);
+		}
+
+		ioloop_nested = io_loop_create();
+		http_client_switch_ioloop(http_client);
+		test_client_requests_switch_ioloop();
+
+		io_loop_run(ioloop_nested);
+
+		io_loop_set_current(prev_ioloop);
+		http_client_switch_ioloop(http_client);
+		test_client_requests_switch_ioloop();
+		io_loop_set_current(ioloop_nested);
+		io_loop_destroy(&ioloop_nested);
+		ioloop_nested = NULL;
+
+		if (debug) {
+			i_debug("test client: echo: leaving ioloop for %u...%u",
+				ioloop_nested_first, ioloop_nested_last);
+		}
+		ioloop_nested_first = ioloop_nested_last = 0;
+	}
 }
 
 static void
@@ -1128,6 +1222,7 @@
 		if (debug)
 			i_debug("server: PID=%s", my_pid);
 		/* child: server */
+		ioloop_nested = FALSE;
 		ioloop = io_loop_create();
 		test_server_init(server_set);
 		io_loop_run(ioloop);
@@ -1139,6 +1234,7 @@
 			i_debug("client: PID=%s", my_pid);
 		i_close_fd(&fd_listen);
 		/* parent: client */
+		ioloop_nested = FALSE;
 		ioloop = io_loop_create();
 		client_init(client_set);
 		io_loop_run(ioloop);
@@ -1247,6 +1343,7 @@
 	blocking = FALSE;
 	request_100_continue = FALSE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_download);
 	test_run_pipeline(test_client_download);
 	test_run_parallel(test_client_download);
@@ -1259,6 +1356,7 @@
 	blocking = TRUE;
 	request_100_continue = FALSE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_download);
 	test_run_pipeline(test_client_download);
 	test_run_parallel(test_client_download);
@@ -1271,6 +1369,7 @@
 	blocking = FALSE;
 	request_100_continue = FALSE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1283,6 +1382,7 @@
 	blocking = TRUE;
 	request_100_continue = FALSE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1295,6 +1395,7 @@
 	blocking = FALSE;
 	request_100_continue = TRUE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1307,6 +1408,7 @@
 	blocking = TRUE;
 	request_100_continue = TRUE;
 	read_server_partial = 0;
+	client_ioloop_nesting = 0;
   test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1319,6 +1421,7 @@
 	blocking = FALSE;
 	request_100_continue = FALSE;
 	read_server_partial = 1024;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1337,6 +1440,7 @@
 	blocking = TRUE;
 	request_100_continue = FALSE;
 	read_server_partial = 1024;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_echo);
 	test_run_pipeline(test_client_echo);
 	test_run_parallel(test_client_echo);
@@ -1356,6 +1460,7 @@
 	request_100_continue = FALSE;
 	read_server_partial = 0;
 	read_client_partial = 1024;
+	client_ioloop_nesting = 0;
 	test_run_sequential(test_client_download);
 	test_run_pipeline(test_client_download);
 	test_run_parallel(test_client_download);
@@ -1371,6 +1476,18 @@
 	test_end();
 }
 
+static void test_download_client_nested_ioloop(void)
+{
+	test_begin("http payload echo (client nested ioloop)");
+	blocking = FALSE;
+	request_100_continue = FALSE;
+	read_server_partial = 0;
+	read_client_partial = 0;
+	client_ioloop_nesting = 10;
+	test_run_parallel(test_client_echo);
+	test_end();
+}
+
 static void (*test_functions[])(void) = {
 	test_download_server_nonblocking,
 	test_download_server_blocking,
@@ -1381,6 +1498,7 @@
 	test_echo_server_nonblocking_partial,
 	test_echo_server_blocking_partial,
 	test_download_client_partial,
+	test_download_client_nested_ioloop,
 	NULL
 };