view src/lib/test-ioloop.c @ 22656:1789bf2a1e01

director: Make sure HOST-RESET-USERS isn't used with max_moving_users=0 The reset command would just hang in that case. doveadm would never have sent this, so this is just an extra sanity check.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Sun, 05 Nov 2017 23:51:56 +0200
parents 2e2563132d5f
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */

#include "test-lib.h"
#include "net.h"
#include "time-util.h"
#include "ioloop.h"
#include "istream.h"

#include <unistd.h>

struct test_ctx {
	bool got_left;
	bool got_right;
	bool got_to;
};

static void timeout_callback(struct timeval *tv)
{
	if (gettimeofday(tv, NULL) < 0)
		i_fatal("gettimeofday() failed: %m");
	io_loop_stop(current_ioloop);
}

static void test_ioloop_fd_cb_left(struct test_ctx *ctx)
{
	ctx->got_left = TRUE;
	if (ctx->got_left && ctx->got_right)
		io_loop_stop(current_ioloop);
}

static void test_ioloop_fd_cb_right(struct test_ctx *ctx)
{
	ctx->got_right = TRUE;
	if (ctx->got_left && ctx->got_right)
		io_loop_stop(current_ioloop);
}

static void test_ioloop_fd_to(struct test_ctx *ctx)
{
	ctx->got_to = TRUE;
	io_loop_stop(current_ioloop);
}

static void test_ioloop_fd(void)
{
	test_begin("ioloop fd");

	struct test_ctx test_ctx;
	int fds[2];
	int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);

	test_assert(ret == 0);
	if (ret < 0) {
		i_error("socketpair() failed: %m");
		test_end();
		return;
	}

	i_zero(&test_ctx);

	struct ioloop *ioloop = io_loop_create();

	struct io *io_left =
			io_add(fds[0], IO_READ,
			       test_ioloop_fd_cb_left, &test_ctx);
	struct io *io_right =
			io_add(fds[1], IO_READ,
				test_ioloop_fd_cb_right, &test_ctx);

	struct timeout *to = timeout_add(2000, test_ioloop_fd_to, &test_ctx);

	if (write(fds[0], "ltr", 3) != 3 ||
	    write(fds[1], "rtl", 3) != 3)
		i_fatal("write() failed: %m");

	io_loop_run(ioloop);

	timeout_remove(&to);
	io_remove(&io_left);
	io_remove(&io_right);

	test_assert(test_ctx.got_to == FALSE);
	test_assert(test_ctx.got_left == TRUE);
	test_assert(test_ctx.got_right == TRUE);

	io_loop_destroy(&ioloop);
	i_close_fd(&fds[0]);
	i_close_fd(&fds[1]);

	test_end();
}

static void test_ioloop_timeout(void)
{
	struct ioloop *ioloop, *ioloop2;
	struct timeout *to, *to2;
	struct timeval tv_start, tv_callback;

	test_begin("ioloop timeout");

	ioloop = io_loop_create();

	/* add a timeout by moving it from another ioloop */
	ioloop2 = io_loop_create();
	to2 = timeout_add(1000, timeout_callback, &tv_callback);
	io_loop_set_current(ioloop);
	to2 = io_loop_move_timeout(&to2);
	io_loop_set_current(ioloop2);
	io_loop_destroy(&ioloop2);

	sleep(1);

	/* add & remove immediately */
	to = timeout_add(1000, timeout_callback, &tv_callback);
	timeout_remove(&to);

	/* add the timeout we're actually testing below */
	to = timeout_add(1000, timeout_callback, &tv_callback);
	if (gettimeofday(&tv_start, NULL) < 0)
		i_fatal("gettimeofday() failed: %m");
	io_loop_run(ioloop);
	test_assert(timeval_diff_msecs(&tv_callback, &tv_start) >= 500);
	timeout_remove(&to);
	timeout_remove(&to2);
	io_loop_destroy(&ioloop);

	test_end();
}

static void io_callback(void *context ATTR_UNUSED)
{
}

static void test_ioloop_find_fd_conditions(void)
{
	struct {
		enum io_condition condition;
		int fd[2];
		struct io *io;
	} tests[] = {
		{ IO_ERROR, { -1, -1 }, NULL },
		{ IO_READ, { -1, -1 }, NULL },
		{ IO_WRITE, { -1, -1 }, NULL },
		{ IO_READ | IO_WRITE, { -1, -1 }, NULL },
		{ IO_READ, { -1, -1 }, NULL } /* read+write as separate ios */
	};
	struct ioloop *ioloop;
	struct io *io;
	unsigned int i;

	test_begin("ioloop find fd conditions");

	ioloop = io_loop_create();

	for (i = 0; i < N_ELEMENTS(tests); i++) {
		if (socketpair(AF_UNIX, SOCK_STREAM, 0, tests[i].fd) < 0)
			i_fatal("socketpair() failed: %m");
		tests[i].io = io_add(tests[i].fd[0], tests[i].condition, io_callback, (void *)NULL);
	}
	io = io_add(tests[i-1].fd[0], IO_WRITE, io_callback, (void *)NULL);
	tests[i-1].condition |= IO_WRITE;

	for (i = 0; i < N_ELEMENTS(tests); i++)
		test_assert_idx(io_loop_find_fd_conditions(ioloop, tests[i].fd[0]) == tests[i].condition, i);

	io_remove(&io);
	for (i = 0; i < N_ELEMENTS(tests); i++) {
		io_remove(&tests[i].io);
		i_close_fd(&tests[i].fd[0]);
		i_close_fd(&tests[i].fd[1]);
	}
	io_loop_destroy(&ioloop);

	test_end();
}

static void io_callback_pending_io(void *context ATTR_UNUSED)
{
	io_loop_stop(current_ioloop);
}

static void test_ioloop_pending_io(void)
{
	test_begin("ioloop pending io");

	struct istream *is = i_stream_create_from_data("data", 4);
	struct ioloop *ioloop = io_loop_create();
	struct io *io = io_add_istream(is, io_callback_pending_io, NULL);
	io_loop_set_current(ioloop);
	io_set_pending(io);
	io_loop_run(ioloop);
	io_remove(&io);
	i_stream_unref(&is);
	io_loop_destroy(&ioloop);

	test_end();
}

void test_ioloop(void)
{
	test_ioloop_timeout();
	test_ioloop_find_fd_conditions();
	test_ioloop_pending_io();
	test_ioloop_fd();
}