/*
 * Copyright © 2014 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <config.h>

#include <check.h>
#include <errno.h>
#include <fcntl.h>
#include <libinput.h>
#include <libinput-util.h>
#include <unistd.h>
#include <stdarg.h>

#include "litest.h"
#include "libinput-util.h"

static int open_restricted(const char *path, int flags, void *data)
{
	int fd = open(path, flags);
	return fd < 0 ? -errno : fd;
}
static void close_restricted(int fd, void *data)
{
	close(fd);
}

static const struct libinput_interface simple_interface = {
	.open_restricted = open_restricted,
	.close_restricted = close_restricted,
};

static struct libevdev_uinput *
create_simple_test_device(const char *name, ...)
{
	va_list args;
	struct libevdev_uinput *uinput;
	struct libevdev *evdev;
	unsigned int type, code;
	int rc;
	struct input_absinfo abs = {
		.value = -1,
		.minimum = 0,
		.maximum = 100,
		.fuzz = 0,
		.flat = 0,
		.resolution = 100,
	};

	evdev = libevdev_new();
	litest_assert_notnull(evdev);
	libevdev_set_name(evdev, name);

	va_start(args, name);

	while ((type = va_arg(args, unsigned int)) != (unsigned int)-1 &&
	       (code = va_arg(args, unsigned int)) != (unsigned int)-1) {
		const struct input_absinfo *a = NULL;
		if (type == EV_ABS)
			a = &abs;
		libevdev_enable_event_code(evdev, type, code, a);
	}

	va_end(args);

	rc = libevdev_uinput_create_from_device(evdev,
						LIBEVDEV_UINPUT_OPEN_MANAGED,
						&uinput);
	litest_assert_int_eq(rc, 0);
	libevdev_free(evdev);

	return uinput;
}

START_TEST(event_conversion_device_notify)
{
	struct libevdev_uinput *uinput;
	struct libinput *li;
	struct libinput_event *event;
	int device_added = 0, device_removed = 0;

	uinput = create_simple_test_device("litest test device",
					   EV_REL, REL_X,
					   EV_REL, REL_Y,
					   EV_KEY, BTN_LEFT,
					   EV_KEY, BTN_MIDDLE,
					   EV_KEY, BTN_LEFT,
					   -1, -1);
	li = libinput_path_create_context(&simple_interface, NULL);
	litest_restore_log_handler(li); /* use the default litest handler */
	libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));

	libinput_dispatch(li);
	libinput_suspend(li);
	libinput_resume(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type == LIBINPUT_EVENT_DEVICE_ADDED ||
		    type == LIBINPUT_EVENT_DEVICE_REMOVED) {
			struct libinput_event_device_notify *dn;
			struct libinput_event *base;
			dn = libinput_event_get_device_notify_event(event);
			base = libinput_event_device_notify_get_base_event(dn);
			ck_assert(event == base);

			if (type == LIBINPUT_EVENT_DEVICE_ADDED)
				device_added++;
			else if (type == LIBINPUT_EVENT_DEVICE_REMOVED)
				device_removed++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}

		libinput_event_destroy(event);
	}

	libinput_unref(li);
	libevdev_uinput_destroy(uinput);

	ck_assert_int_gt(device_added, 0);
	ck_assert_int_gt(device_removed, 0);
}
END_TEST

START_TEST(event_conversion_pointer)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int motion = 0, button = 0;

	/* Queue at least two relative motion events as the first one may
	 * be absorbed by the pointer acceleration filter. */
	litest_event(dev, EV_REL, REL_X, -1);
	litest_event(dev, EV_REL, REL_Y, -1);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);
	litest_event(dev, EV_REL, REL_X, -1);
	litest_event(dev, EV_REL, REL_Y, -1);
	litest_event(dev, EV_KEY, BTN_LEFT, 1);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type == LIBINPUT_EVENT_POINTER_MOTION ||
		    type == LIBINPUT_EVENT_POINTER_BUTTON) {
			struct libinput_event_pointer *p;
			struct libinput_event *base;
			p = libinput_event_get_pointer_event(event);
			base = libinput_event_pointer_get_base_event(p);
			ck_assert(event == base);

			if (type == LIBINPUT_EVENT_POINTER_MOTION)
				motion++;
			else if (type == LIBINPUT_EVENT_POINTER_BUTTON)
				button++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(motion, 0);
	ck_assert_int_gt(button, 0);
}
END_TEST

START_TEST(event_conversion_pointer_abs)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int motion = 0, button = 0;

	litest_event(dev, EV_ABS, ABS_X, 10);
	litest_event(dev, EV_ABS, ABS_Y, 50);
	litest_event(dev, EV_KEY, BTN_LEFT, 1);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);
	litest_event(dev, EV_ABS, ABS_X, 30);
	litest_event(dev, EV_ABS, ABS_Y, 30);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type == LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE ||
		    type == LIBINPUT_EVENT_POINTER_BUTTON) {
			struct libinput_event_pointer *p;
			struct libinput_event *base;
			p = libinput_event_get_pointer_event(event);
			base = libinput_event_pointer_get_base_event(p);
			ck_assert(event == base);

			if (type == LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE)
				motion++;
			else if (type == LIBINPUT_EVENT_POINTER_BUTTON)
				button++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(motion, 0);
	ck_assert_int_gt(button, 0);
}
END_TEST

START_TEST(event_conversion_key)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int key = 0;

	litest_event(dev, EV_KEY, KEY_A, 1);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);
	litest_event(dev, EV_KEY, KEY_A, 0);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type == LIBINPUT_EVENT_KEYBOARD_KEY) {
			struct libinput_event_keyboard *k;
			struct libinput_event *base;
			k = libinput_event_get_keyboard_event(event);
			base = libinput_event_keyboard_get_base_event(k);
			ck_assert(event == base);

			key++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(key, 0);
}
END_TEST

START_TEST(event_conversion_touch)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int touch = 0;

	libinput_dispatch(li);

	litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
	litest_event(dev, EV_KEY, BTN_TOUCH, 1);
	litest_event(dev, EV_ABS, ABS_X, 10);
	litest_event(dev, EV_ABS, ABS_Y, 10);
	litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
	litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 1);
	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 10);
	litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 10);
	litest_event(dev, EV_SYN, SYN_REPORT, 0);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type >= LIBINPUT_EVENT_TOUCH_DOWN &&
		    type <= LIBINPUT_EVENT_TOUCH_FRAME) {
			struct libinput_event_touch *t;
			struct libinput_event *base;
			t = libinput_event_get_touch_event(event);
			base = libinput_event_touch_get_base_event(t);
			ck_assert(event == base);

			touch++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(touch, 0);
}
END_TEST

START_TEST(event_conversion_gesture)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int gestures = 0;
	int i;

	libinput_dispatch(li);

	litest_touch_down(dev, 0, 70, 30);
	litest_touch_down(dev, 1, 30, 70);
	for (i = 0; i < 8; i++) {
		litest_push_event_frame(dev);
		litest_touch_move(dev, 0, 70 - i * 5, 30 + i * 5);
		litest_touch_move(dev, 1, 30 + i * 5, 70 - i * 5);
		litest_pop_event_frame(dev);
		libinput_dispatch(li);
	}

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type >= LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN &&
		    type <= LIBINPUT_EVENT_GESTURE_PINCH_END) {
			struct libinput_event_gesture *g;
			struct libinput_event *base;
			g = libinput_event_get_gesture_event(event);
			base = libinput_event_gesture_get_base_event(g);
			ck_assert(event == base);

			gestures++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(gestures, 0);
}
END_TEST

START_TEST(event_conversion_tablet)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int events = 0;
	struct axis_replacement axes[] = {
		{ ABS_DISTANCE, 10 },
		{ -1, -1 }
	};

	litest_tablet_proximity_in(dev, 50, 50, axes);
	litest_tablet_motion(dev, 60, 50, axes);
	litest_button_click(dev, BTN_STYLUS, true);
	litest_button_click(dev, BTN_STYLUS, false);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type >= LIBINPUT_EVENT_TABLET_TOOL_AXIS &&
		    type <= LIBINPUT_EVENT_TABLET_TOOL_BUTTON) {
			struct libinput_event_tablet_tool *t;
			struct libinput_event *base;
			t = libinput_event_get_tablet_tool_event(event);
			base = libinput_event_tablet_tool_get_base_event(t);
			ck_assert(event == base);

			events++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(events, 0);
}
END_TEST

START_TEST(event_conversion_tablet_pad)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int events = 0;

	litest_button_click(dev, BTN_0, true);
	litest_pad_ring_start(dev, 10);
	litest_pad_ring_end(dev);

	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type >= LIBINPUT_EVENT_TABLET_PAD_BUTTON &&
		    type <= LIBINPUT_EVENT_TABLET_PAD_STRIP) {
			struct libinput_event_tablet_pad *p;
			struct libinput_event *base;

			p = libinput_event_get_tablet_pad_event(event);
			base = libinput_event_tablet_pad_get_base_event(p);
			ck_assert(event == base);

			events++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_switch_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(events, 0);
}
END_TEST

START_TEST(event_conversion_switch)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	struct libinput_event *event;
	int sw = 0;

	litest_switch_action(dev,
			     LIBINPUT_SWITCH_LID,
			     LIBINPUT_SWITCH_STATE_ON);
	litest_switch_action(dev,
			     LIBINPUT_SWITCH_LID,
			     LIBINPUT_SWITCH_STATE_OFF);
	libinput_dispatch(li);

	while ((event = libinput_get_event(li))) {
		enum libinput_event_type type;
		type = libinput_event_get_type(event);

		if (type == LIBINPUT_EVENT_SWITCH_TOGGLE) {
			struct libinput_event_switch *s;
			struct libinput_event *base;
			s = libinput_event_get_switch_event(event);
			base = libinput_event_switch_get_base_event(s);
			ck_assert(event == base);

			sw++;

			litest_disable_log_handler(li);
			ck_assert(libinput_event_get_device_notify_event(event) == NULL);
			ck_assert(libinput_event_get_keyboard_event(event) == NULL);
			ck_assert(libinput_event_get_pointer_event(event) == NULL);
			ck_assert(libinput_event_get_touch_event(event) == NULL);
			ck_assert(libinput_event_get_gesture_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
			ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
			litest_restore_log_handler(li);
		}
		libinput_event_destroy(event);
	}

	ck_assert_int_gt(sw, 0);
}
END_TEST

START_TEST(bitfield_helpers)
{
	/* This value has a bit set on all of the word boundaries we want to
	 * test: 0, 1, 7, 8, 31, 32, and 33
	 */
	unsigned char read_bitfield[] = { 0x83, 0x1, 0x0, 0x80, 0x3 };
	unsigned char write_bitfield[ARRAY_LENGTH(read_bitfield)] = {0};
	size_t i;

	/* Now check that the bitfield we wrote to came out to be the same as
	 * the bitfield we were writing from */
	for (i = 0; i < ARRAY_LENGTH(read_bitfield) * 8; i++) {
		switch (i) {
		case 0:
		case 1:
		case 7:
		case 8:
		case 31:
		case 32:
		case 33:
			ck_assert(bit_is_set(read_bitfield, i));
			set_bit(write_bitfield, i);
			break;
		default:
			ck_assert(!bit_is_set(read_bitfield, i));
			clear_bit(write_bitfield, i);
			break;
		}
	}

	ck_assert_int_eq(memcmp(read_bitfield,
				write_bitfield,
				sizeof(read_bitfield)),
			 0);
}
END_TEST

START_TEST(context_ref_counting)
{
	struct libinput *li;

	/* These tests rely on valgrind to detect memory leak and use after
	 * free errors. */

	li = libinput_path_create_context(&simple_interface, NULL);
	ck_assert_notnull(li);
	ck_assert_ptr_eq(libinput_unref(li), NULL);

	li = libinput_path_create_context(&simple_interface, NULL);
	ck_assert_notnull(li);
	ck_assert_ptr_eq(libinput_ref(li), li);
	ck_assert_ptr_eq(libinput_unref(li), li);
	ck_assert_ptr_eq(libinput_unref(li), NULL);
}
END_TEST

START_TEST(config_status_string)
{
	const char *strs[3];
	const char *invalid;
	size_t i, j;

	strs[0] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_SUCCESS);
	strs[1] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
	strs[2] = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_INVALID);

	for (i = 0; i < ARRAY_LENGTH(strs) - 1; i++)
		for (j = i + 1; j < ARRAY_LENGTH(strs); j++)
			ck_assert_str_ne(strs[i], strs[j]);

	invalid = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_INVALID + 1);
	ck_assert(invalid == NULL);
	invalid = libinput_config_status_to_str(LIBINPUT_CONFIG_STATUS_SUCCESS - 1);
	ck_assert(invalid == NULL);
}
END_TEST

START_TEST(matrix_helpers)
{
	struct matrix m1, m2, m3;
	float f[6] = { 1, 2, 3, 4, 5, 6 };
	int x, y;
	int row, col;

	matrix_init_identity(&m1);

	for (row = 0; row < 3; row++) {
		for (col = 0; col < 3; col++) {
			ck_assert_int_eq(m1.val[row][col],
					 (row == col) ? 1 : 0);
		}
	}
	ck_assert(matrix_is_identity(&m1));

	matrix_from_farray6(&m2, f);
	ck_assert_int_eq(m2.val[0][0], 1);
	ck_assert_int_eq(m2.val[0][1], 2);
	ck_assert_int_eq(m2.val[0][2], 3);
	ck_assert_int_eq(m2.val[1][0], 4);
	ck_assert_int_eq(m2.val[1][1], 5);
	ck_assert_int_eq(m2.val[1][2], 6);
	ck_assert_int_eq(m2.val[2][0], 0);
	ck_assert_int_eq(m2.val[2][1], 0);
	ck_assert_int_eq(m2.val[2][2], 1);

	x = 100;
	y = 5;
	matrix_mult_vec(&m1, &x, &y);
	ck_assert_int_eq(x, 100);
	ck_assert_int_eq(y, 5);

	matrix_mult(&m3, &m1, &m1);
	ck_assert(matrix_is_identity(&m3));

	matrix_init_scale(&m2, 2, 4);
	ck_assert_int_eq(m2.val[0][0], 2);
	ck_assert_int_eq(m2.val[0][1], 0);
	ck_assert_int_eq(m2.val[0][2], 0);
	ck_assert_int_eq(m2.val[1][0], 0);
	ck_assert_int_eq(m2.val[1][1], 4);
	ck_assert_int_eq(m2.val[1][2], 0);
	ck_assert_int_eq(m2.val[2][0], 0);
	ck_assert_int_eq(m2.val[2][1], 0);
	ck_assert_int_eq(m2.val[2][2], 1);

	matrix_mult_vec(&m2, &x, &y);
	ck_assert_int_eq(x, 200);
	ck_assert_int_eq(y, 20);

	matrix_init_translate(&m2, 10, 100);
	ck_assert_int_eq(m2.val[0][0], 1);
	ck_assert_int_eq(m2.val[0][1], 0);
	ck_assert_int_eq(m2.val[0][2], 10);
	ck_assert_int_eq(m2.val[1][0], 0);
	ck_assert_int_eq(m2.val[1][1], 1);
	ck_assert_int_eq(m2.val[1][2], 100);
	ck_assert_int_eq(m2.val[2][0], 0);
	ck_assert_int_eq(m2.val[2][1], 0);
	ck_assert_int_eq(m2.val[2][2], 1);

	matrix_mult_vec(&m2, &x, &y);
	ck_assert_int_eq(x, 210);
	ck_assert_int_eq(y, 120);

	matrix_to_farray6(&m2, f);
	ck_assert_int_eq(f[0], 1);
	ck_assert_int_eq(f[1], 0);
	ck_assert_int_eq(f[2], 10);
	ck_assert_int_eq(f[3], 0);
	ck_assert_int_eq(f[4], 1);
	ck_assert_int_eq(f[5], 100);
}
END_TEST

START_TEST(ratelimit_helpers)
{
	struct ratelimit rl;
	unsigned int i, j;

	/* 10 attempts every 100ms */
	ratelimit_init(&rl, ms2us(100), 10);

	for (j = 0; j < 3; ++j) {
		/* a burst of 9 attempts must succeed */
		for (i = 0; i < 9; ++i) {
			ck_assert_int_eq(ratelimit_test(&rl),
					 RATELIMIT_PASS);
		}

		/* the 10th attempt reaches the threshold */
		ck_assert_int_eq(ratelimit_test(&rl), RATELIMIT_THRESHOLD);

		/* ..then further attempts must fail.. */
		ck_assert_int_eq(ratelimit_test(&rl), RATELIMIT_EXCEEDED);

		/* ..regardless of how often we try. */
		for (i = 0; i < 100; ++i) {
			ck_assert_int_eq(ratelimit_test(&rl),
					 RATELIMIT_EXCEEDED);
		}

		/* ..even after waiting 20ms */
		msleep(20);
		for (i = 0; i < 100; ++i) {
			ck_assert_int_eq(ratelimit_test(&rl),
					 RATELIMIT_EXCEEDED);
		}

		/* but after 100ms the counter is reset */
		msleep(90); /* +10ms to account for time drifts */
	}
}
END_TEST

struct parser_test {
	char *tag;
	int expected_value;
};

START_TEST(dpi_parser)
{
	struct parser_test tests[] = {
		{ "450 *1800 3200", 1800 },
		{ "*450 1800 3200", 450 },
		{ "450 1800 *3200", 3200 },
		{ "450 1800 3200", 3200 },
		{ "450 1800 failboat", 0 },
		{ "450 1800 *failboat", 0 },
		{ "0 450 1800 *3200", 0 },
		{ "450@37 1800@12 *3200@6", 3200 },
		{ "450@125 1800@125   *3200@125  ", 3200 },
		{ "450@125 *1800@125  3200@125", 1800 },
		{ "*this @string fails", 0 },
		{ "12@34 *45@", 0 },
		{ "12@a *45@", 0 },
		{ "12@a *45@25", 0 },
		{ "                                      * 12, 450, 800", 0 },
		{ "                                      *12, 450, 800", 12 },
		{ "*12, *450, 800", 12 },
		{ "*-23412, 450, 800", 0 },
		{ "112@125, 450@125, 800@125, 900@-125", 0 },
		{ "", 0 },
		{ "   ", 0 },
		{ "* ", 0 },
		{ NULL, 0 }
	};
	int i, dpi;

	for (i = 0; tests[i].tag != NULL; i++) {
		dpi = parse_mouse_dpi_property(tests[i].tag);
		ck_assert_int_eq(dpi, tests[i].expected_value);
	}

	dpi = parse_mouse_dpi_property(NULL);
	ck_assert_int_eq(dpi, 0);
}
END_TEST

START_TEST(wheel_click_parser)
{
	struct parser_test tests[] = {
		{ "1", 1 },
		{ "10", 10 },
		{ "-12", -12 },
		{ "360", 360 },

		{ "0", 0 },
		{ "-0", 0 },
		{ "a", 0 },
		{ "10a", 0 },
		{ "10-", 0 },
		{ "sadfasfd", 0 },
		{ "361", 0 },
		{ NULL, 0 }
	};

	int i, angle;

	for (i = 0; tests[i].tag != NULL; i++) {
		angle = parse_mouse_wheel_click_angle_property(tests[i].tag);
		ck_assert_int_eq(angle, tests[i].expected_value);
	}
}
END_TEST

START_TEST(wheel_click_count_parser)
{
	struct parser_test tests[] = {
		{ "1", 1 },
		{ "10", 10 },
		{ "-12", -12 },
		{ "360", 360 },

		{ "0", 0 },
		{ "-0", 0 },
		{ "a", 0 },
		{ "10a", 0 },
		{ "10-", 0 },
		{ "sadfasfd", 0 },
		{ "361", 0 },
		{ NULL, 0 }
	};

	int i, angle;

	for (i = 0; tests[i].tag != NULL; i++) {
		angle = parse_mouse_wheel_click_count_property(tests[i].tag);
		ck_assert_int_eq(angle, tests[i].expected_value);
	}

	angle = parse_mouse_wheel_click_count_property(NULL);
	ck_assert_int_eq(angle, 0);
}
END_TEST

struct parser_test_float {
	char *tag;
	double expected_value;
};

START_TEST(trackpoint_accel_parser)
{
	struct parser_test_float tests[] = {
		{ "0.5", 0.5 },
		{ "1.0", 1.0 },
		{ "2.0", 2.0 },
		{ "fail1.0", 0.0 },
		{ "1.0fail", 0.0 },
		{ "0,5", 0.0 },
		{ NULL, 0.0 }
	};
	int i;
	double accel;

	for (i = 0; tests[i].tag != NULL; i++) {
		accel = parse_trackpoint_accel_property(tests[i].tag);
		ck_assert(accel == tests[i].expected_value);
	}

	accel = parse_trackpoint_accel_property(NULL);
	ck_assert_double_eq(accel, 0.0);
}
END_TEST

struct parser_test_dimension {
	char *tag;
	bool success;
	int x, y;
};

START_TEST(dimension_prop_parser)
{
	struct parser_test_dimension tests[] = {
		{ "10x10", true, 10, 10 },
		{ "1x20", true, 1, 20 },
		{ "1x8000", true, 1, 8000 },
		{ "238492x428210", true, 238492, 428210 },
		{ "0x0", true, 0, 0 },
		{ "-10x10", false, 0, 0 },
		{ "-1", false, 0, 0 },
		{ "1x-99", false, 0, 0 },
		{ "0", false, 0, 0 },
		{ "100", false, 0, 0 },
		{ "", false, 0, 0 },
		{ "abd", false, 0, 0 },
		{ "xabd", false, 0, 0 },
		{ "0xaf", false, 0, 0 },
		{ "0x0x", true, 0, 0 },
		{ "x10", false, 0, 0 },
		{ NULL, false, 0, 0 }
	};
	int i;
	size_t x, y;
	bool success;

	for (i = 0; tests[i].tag != NULL; i++) {
		x = y = 0xad;
		success = parse_dimension_property(tests[i].tag, &x, &y);
		ck_assert(success == tests[i].success);
		if (success) {
			ck_assert_int_eq(x, tests[i].x);
			ck_assert_int_eq(y, tests[i].y);
		} else {
			ck_assert_int_eq(x, 0xad);
			ck_assert_int_eq(y, 0xad);
		}
	}

	success = parse_dimension_property(NULL, &x, &y);
	ck_assert(success == false);
}
END_TEST

struct parser_test_reliability {
	char *tag;
	bool success;
	enum switch_reliability reliability;
};

START_TEST(reliability_prop_parser)
{
	struct parser_test_reliability tests[] = {
		{ "reliable", true, RELIABILITY_RELIABLE },
		{ "unreliable", false, 0 },
		{ "", false, 0 },
		{ "0", false, 0 },
		{ "1", false, 0 },
		{ NULL, false, 0, }
	};
	enum switch_reliability r;
	bool success;
	int i;

	for (i = 0; tests[i].tag != NULL; i++) {
		r = 0xaf;
		success = parse_switch_reliability_property(tests[i].tag, &r);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(r, tests[i].reliability);
		else
			ck_assert_int_eq(r, 0xaf);
	}

	success = parse_switch_reliability_property(NULL, &r);
	ck_assert(success == true);
	ck_assert_int_eq(r, RELIABILITY_UNKNOWN);

	success = parse_switch_reliability_property("foo", NULL);
	ck_assert(success == false);
}
END_TEST

struct parser_test_calibration {
	char *prop;
	bool success;
	float values[6];
};

START_TEST(calibration_prop_parser)
{
#define DEFAULT_VALUES { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }
	const float untouched[6] = DEFAULT_VALUES;
	struct parser_test_calibration tests[] = {
		{ "", false, DEFAULT_VALUES },
		{ "banana", false, DEFAULT_VALUES },
		{ "1 2 3 a 5 6", false, DEFAULT_VALUES },
		{ "2", false, DEFAULT_VALUES },
		{ "2 3 4 5 6", false, DEFAULT_VALUES },
		{ "1 2 3 4 5 6", true, DEFAULT_VALUES },
		{ "6.00012 3.244 4.238 5.2421 6.0134 8.860", true,
			{ 6.00012, 3.244, 4.238, 5.2421, 6.0134, 8.860 }},
		{ "0xff 2 3 4 5 6", true,
			{ 255, 2, 3, 4, 5, 6 }},
		{ NULL, false, DEFAULT_VALUES }
	};
	bool success;
	float calibration[6];
	int rc;
	int i;

	for (i = 0; tests[i].prop != NULL; i++) {
		memcpy(calibration, untouched, sizeof(calibration));

		success = parse_calibration_property(tests[i].prop,
						     calibration);
		ck_assert_int_eq(success, tests[i].success);
		if (success)
			rc = memcmp(tests[i].values,
				    calibration,
				    sizeof(calibration));
		else
			rc = memcmp(untouched,
				    calibration,
				    sizeof(calibration));
		ck_assert_int_eq(rc, 0);
	}

	memcpy(calibration, untouched, sizeof(calibration));

	success = parse_calibration_property(NULL, calibration);
	ck_assert(success == false);
	rc = memcmp(untouched, calibration, sizeof(calibration));
	ck_assert_int_eq(rc, 0);
}
END_TEST

struct parser_test_range {
	char *tag;
	bool success;
	int hi, lo;
};

START_TEST(range_prop_parser)
{
	struct parser_test_range tests[] = {
		{ "10:8", true, 10, 8 },
		{ "100:-1", true, 100, -1 },
		{ "-203813:-502023", true, -203813, -502023 },
		{ "238492:28210", true, 238492, 28210 },
		{ "none", true, 0, 0 },
		{ "0:0", false, 0, 0 },
		{ "", false, 0, 0 },
		{ "abcd", false, 0, 0 },
		{ "10:30:10", false, 0, 0 },
		{ NULL, false, 0, 0 }
	};
	int i;
	int hi, lo;
	bool success;

	for (i = 0; tests[i].tag != NULL; i++) {
		hi = lo = 0xad;
		success = parse_range_property(tests[i].tag, &hi, &lo);
		ck_assert(success == tests[i].success);
		if (success) {
			ck_assert_int_eq(hi, tests[i].hi);
			ck_assert_int_eq(lo, tests[i].lo);
		} else {
			ck_assert_int_eq(hi, 0xad);
			ck_assert_int_eq(lo, 0xad);
		}
	}

	success = parse_range_property(NULL, NULL, NULL);
	ck_assert(success == false);
}
END_TEST

START_TEST(palm_pressure_parser)
{
	struct parser_test tests[] = {
		{ "1", 1 },
		{ "10", 10 },
		{ "255", 255 },

		{ "-12", 0 },
		{ "360", 0 },
		{ "0", 0 },
		{ "-0", 0 },
		{ "a", 0 },
		{ "10a", 0 },
		{ "10-", 0 },
		{ "sadfasfd", 0 },
		{ "361", 0 },
		{ NULL, 0 }
	};

	int i, angle;

	for (i = 0; tests[i].tag != NULL; i++) {
		angle = parse_palm_pressure_property(tests[i].tag);
		ck_assert_int_eq(angle, tests[i].expected_value);
	}
}
END_TEST

START_TEST(time_conversion)
{
	ck_assert_int_eq(us(10), 10);
	ck_assert_int_eq(ns2us(10000), 10);
	ck_assert_int_eq(ms2us(10), 10000);
	ck_assert_int_eq(s2us(1), 1000000);
	ck_assert_int_eq(us2ms(10000), 10);
}
END_TEST

struct atoi_test {
	char *str;
	bool success;
	int val;
};

START_TEST(safe_atoi_test)
{
	struct atoi_test tests[] = {
		{ "10", true, 10 },
		{ "20", true, 20 },
		{ "-1", true, -1 },
		{ "2147483647", true, 2147483647 },
		{ "-2147483648", true, -2147483648 },
		{ "4294967295", false, 0 },
		{ "0x0", false, 0 },
		{ "-10x10", false, 0 },
		{ "1x-99", false, 0 },
		{ "", false, 0 },
		{ "abd", false, 0 },
		{ "xabd", false, 0 },
		{ "0xaf", false, 0 },
		{ "0x0x", false, 0 },
		{ "x10", false, 0 },
		{ NULL, false, 0 }
	};
	int v;
	bool success;

	for (int i = 0; tests[i].str != NULL; i++) {
		v = 0xad;
		success = safe_atoi(tests[i].str, &v);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(v, tests[i].val);
		else
			ck_assert_int_eq(v, 0xad);
	}
}
END_TEST

START_TEST(safe_atoi_base_16_test)
{
	struct atoi_test tests[] = {
		{ "10", true, 0x10 },
		{ "20", true, 0x20 },
		{ "-1", true, -1 },
		{ "0x10", true, 0x10 },
		{ "0xff", true, 0xff },
		{ "abc", true, 0xabc },
		{ "-10", true, -0x10 },
		{ "0x0", true, 0 },
		{ "0", true, 0 },
		{ "0x-99", false, 0 },
		{ "0xak", false, 0 },
		{ "0x", false, 0 },
		{ "x10", false, 0 },
		{ NULL, false, 0 }
	};

	int v;
	bool success;

	for (int i = 0; tests[i].str != NULL; i++) {
		v = 0xad;
		success = safe_atoi_base(tests[i].str, &v, 16);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(v, tests[i].val);
		else
			ck_assert_int_eq(v, 0xad);
	}
}
END_TEST

START_TEST(safe_atoi_base_8_test)
{
	struct atoi_test tests[] = {
		{ "7", true, 07 },
		{ "10", true, 010 },
		{ "20", true, 020 },
		{ "-1", true, -1 },
		{ "010", true, 010 },
		{ "0ff", false, 0 },
		{ "abc", false, 0},
		{ "0xabc", false, 0},
		{ "-10", true, -010 },
		{ "0", true, 0 },
		{ "00", true, 0 },
		{ "0x0", false, 0 },
		{ "0x-99", false, 0 },
		{ "0xak", false, 0 },
		{ "0x", false, 0 },
		{ "x10", false, 0 },
		{ NULL, false, 0 }
	};

	int v;
	bool success;

	for (int i = 0; tests[i].str != NULL; i++) {
		v = 0xad;
		success = safe_atoi_base(tests[i].str, &v, 8);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(v, tests[i].val);
		else
			ck_assert_int_eq(v, 0xad);
	}
}
END_TEST

struct atod_test {
	char *str;
	bool success;
	double val;
};

START_TEST(safe_atod_test)
{
	struct atod_test tests[] = {
		{ "10", true, 10 },
		{ "20", true, 20 },
		{ "-1", true, -1 },
		{ "2147483647", true, 2147483647 },
		{ "-2147483648", true, -2147483648 },
		{ "4294967295", true, 4294967295 },
		{ "0x0", true, 0 },
		{ "0x10", true, 0x10 },
		{ "0xaf", true, 0xaf },
		{ "x80", false, 0 },
		{ "0.0", true, 0.0 },
		{ "0.1", true, 0.1 },
		{ "1.2", true, 1.2 },
		{ "-324.9", true, -324.9 },
		{ "9324.9", true, 9324.9 },
		{ "NAN", false, 0 },
		{ "INFINITY", false, 0 },
		{ "-10x10", false, 0 },
		{ "1x-99", false, 0 },
		{ "", false, 0 },
		{ "abd", false, 0 },
		{ "xabd", false, 0 },
		{ "0x0x", false, 0 },
		{ NULL, false, 0 }
	};
	double v;
	bool success;

	for (int i = 0; tests[i].str != NULL; i++) {
		v = 0xad;
		success = safe_atod(tests[i].str, &v);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(v, tests[i].val);
		else
			ck_assert_int_eq(v, 0xad);
	}
}
END_TEST

struct strsplit_test {
	const char *string;
	const char *delim;
	const char *results[10];
};

START_TEST(strsplit_test)
{
	struct strsplit_test tests[] = {
		{ "one two three", " ", { "one", "two", "three", NULL } },
		{ "one", " ", { "one", NULL } },
		{ "one two ", " ", { "one", "two", NULL } },
		{ "one  two", " ", { "one", "two", NULL } },
		{ " one two", " ", { "one", "two", NULL } },
		{ "one", "\t \r", { "one", NULL } },
		{ "one two three", " t", { "one", "wo", "hree", NULL } },
		{ " one two three", "te", { " on", " ", "wo ", "hr", NULL } },
		{ "one", "ne", { "o", NULL } },
		{ "onene", "ne", { "o", NULL } },
		{ NULL, NULL, { NULL }}
	};
	struct strsplit_test *t = tests;

	while (t->string) {
		char **strv;
		int idx = 0;
		strv = strv_from_string(t->string, t->delim);
		while (t->results[idx]) {
			ck_assert_str_eq(t->results[idx], strv[idx]);
			idx++;
		}
		ck_assert_ptr_eq(strv[idx], NULL);
		strv_free(strv);
		t++;
	}

	/* Special cases */
	ck_assert_ptr_eq(strv_from_string("", " "), NULL);
	ck_assert_ptr_eq(strv_from_string(" ", " "), NULL);
	ck_assert_ptr_eq(strv_from_string("     ", " "), NULL);
	ck_assert_ptr_eq(strv_from_string("oneoneone", "one"), NULL);
}
END_TEST

static int open_restricted_leak(const char *path, int flags, void *data)
{
	return *(int*)data;
}

static void close_restricted_leak(int fd, void *data)
{
	/* noop */
}

const struct libinput_interface leak_interface = {
	.open_restricted = open_restricted_leak,
	.close_restricted = close_restricted_leak,
};

START_TEST(fd_no_event_leak)
{
	struct libevdev_uinput *uinput;
	struct libinput *li;
	struct libinput_device *device;
	int fd = -1;
	const char *path;
	struct libinput_event *event;

	uinput = create_simple_test_device("litest test device",
					   EV_REL, REL_X,
					   EV_REL, REL_Y,
					   EV_KEY, BTN_LEFT,
					   EV_KEY, BTN_MIDDLE,
					   EV_KEY, BTN_LEFT,
					   -1, -1);
	path = libevdev_uinput_get_devnode(uinput);

	fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
	ck_assert_int_gt(fd, -1);

	li = libinput_path_create_context(&leak_interface, &fd);
	litest_restore_log_handler(li); /* use the default litest handler */

	/* Add the device, trigger an event, then remove it again.
	 * Without it, we get a SYN_DROPPED immediately and no events.
	 */
	device = libinput_path_add_device(li, path);
	libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1);
	libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
	libinput_path_remove_device(device);
	libinput_dispatch(li);
	litest_drain_events(li);

	/* Device is removed, but fd is still open. Queue an event, add a
	 * new device with the same fd, the queued event must be discarded
	 * by libinput */
	libevdev_uinput_write_event(uinput, EV_REL, REL_Y, 1);
	libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
	libinput_dispatch(li);

	libinput_path_add_device(li, path);
	libinput_dispatch(li);
	event = libinput_get_event(li);
	ck_assert_int_eq(libinput_event_get_type(event),
			 LIBINPUT_EVENT_DEVICE_ADDED);
	libinput_event_destroy(event);

	litest_assert_empty_queue(li);

	close(fd);
	libinput_unref(li);
	libevdev_uinput_destroy(uinput);
}
END_TEST

START_TEST(library_version)
{
	const char *version = LIBINPUT_LT_VERSION;
	int C, R, A;
	int rc;

	rc = sscanf(version, "%d:%d:%d", &C, &R, &A);
	ck_assert_int_eq(rc, 3);

	ck_assert_int_ge(C, 17);
	ck_assert_int_ge(R, 0);
	ck_assert_int_ge(A, 7);

	/* Binary compatibility broken? */
	ck_assert(R != 0 || A != 0);

	/* The first stable API in 0.12 had 10:0:0  */
	ck_assert_int_eq(C - A, 10);
}
END_TEST

static void timer_offset_warning(struct libinput *libinput,
				 enum libinput_log_priority priority,
				 const char *format,
				 va_list args)
{
	int *warning_triggered = (int*)libinput_get_user_data(libinput);

	if (priority == LIBINPUT_LOG_PRIORITY_ERROR &&
	    strstr(format, "offset negative"))
		(*warning_triggered)++;
}

START_TEST(timer_offset_bug_warning)
{
	struct litest_device *dev = litest_current_device();
	struct libinput *li = dev->libinput;
	int warning_triggered = 0;

	litest_enable_tap(dev->libinput_device);
	litest_drain_events(li);

	litest_touch_down(dev, 0, 50, 50);
	litest_touch_up(dev, 0);

	litest_timeout_tap();

	libinput_set_user_data(li, &warning_triggered);
	libinput_log_set_handler(li, timer_offset_warning);
	libinput_dispatch(li);

	/* triggered for touch down and touch up */
	ck_assert_int_eq(warning_triggered, 2);
	litest_restore_log_handler(li);
}
END_TEST

START_TEST(timer_flush)
{
	struct libinput *li;
	struct litest_device *keyboard, *touchpad;

	li = litest_create_context();

	touchpad = litest_add_device(li, LITEST_SYNAPTICS_TOUCHPAD);
	litest_enable_tap(touchpad->libinput_device);
	libinput_dispatch(li);
	keyboard = litest_add_device(li, LITEST_KEYBOARD);
	libinput_dispatch(li);
	litest_drain_events(li);

	/* make sure tapping works */
	litest_touch_down(touchpad, 0, 50, 50);
	litest_touch_up(touchpad, 0);
	libinput_dispatch(li);
	litest_timeout_tap();
	libinput_dispatch(li);

	litest_assert_button_event(li, BTN_LEFT,
				   LIBINPUT_BUTTON_STATE_PRESSED);
	litest_assert_button_event(li, BTN_LEFT,
				   LIBINPUT_BUTTON_STATE_RELEASED);
	litest_assert_empty_queue(li);

	/* make sure dwt-tap is ignored */
	litest_keyboard_key(keyboard, KEY_A, true);
	litest_keyboard_key(keyboard, KEY_A, false);
	libinput_dispatch(li);
	litest_touch_down(touchpad, 0, 50, 50);
	litest_touch_up(touchpad, 0);
	libinput_dispatch(li);
	litest_timeout_tap();
	libinput_dispatch(li);
	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);

	/* Ingore 'timer offset negative' warnings */
	litest_disable_log_handler(li);

	/* now mess with the timing
	   - send a key event
	   - expire dwt
	   - send a tap
	   and then call libinput_dispatch(). libinput should notice that
	   the tap event came in after the timeout and thus acknowledge the
	   tap.
	 */
	litest_keyboard_key(keyboard, KEY_A, true);
	litest_keyboard_key(keyboard, KEY_A, false);
	litest_timeout_dwt_long();
	litest_touch_down(touchpad, 0, 50, 50);
	litest_touch_up(touchpad, 0);
	libinput_dispatch(li);
	litest_timeout_tap();
	libinput_dispatch(li);
	litest_restore_log_handler(li);

	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
	litest_assert_button_event(li, BTN_LEFT,
				   LIBINPUT_BUTTON_STATE_PRESSED);
	litest_assert_button_event(li, BTN_LEFT,
				   LIBINPUT_BUTTON_STATE_RELEASED);

	litest_delete_device(keyboard);
	litest_delete_device(touchpad);
	libinput_unref(li);
}
END_TEST

void
litest_setup_tests_misc(void)
{
	litest_add_no_device("events:conversion", event_conversion_device_notify);
	litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
	litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
	litest_add_for_device("events:conversion", event_conversion_pointer_abs, LITEST_XEN_VIRTUAL_POINTER);
	litest_add_for_device("events:conversion", event_conversion_key, LITEST_KEYBOARD);
	litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH);
	litest_add_for_device("events:conversion", event_conversion_gesture, LITEST_BCM5974);
	litest_add_for_device("events:conversion", event_conversion_tablet, LITEST_WACOM_CINTIQ);
	litest_add_for_device("events:conversion", event_conversion_tablet_pad, LITEST_WACOM_INTUOS5_PAD);
	litest_add_for_device("events:conversion", event_conversion_switch, LITEST_LID_SWITCH);
	litest_add_no_device("misc:bitfield_helpers", bitfield_helpers);

	litest_add_no_device("context:refcount", context_ref_counting);
	litest_add_no_device("config:status string", config_status_string);

	litest_add_for_device("timer:offset-warning", timer_offset_bug_warning, LITEST_SYNAPTICS_TOUCHPAD);
	litest_add_no_device("timer:flush", timer_flush);

	litest_add_no_device("misc:matrix", matrix_helpers);
	litest_add_no_device("misc:ratelimit", ratelimit_helpers);
	litest_add_no_device("misc:parser", dpi_parser);
	litest_add_no_device("misc:parser", wheel_click_parser);
	litest_add_no_device("misc:parser", wheel_click_count_parser);
	litest_add_no_device("misc:parser", trackpoint_accel_parser);
	litest_add_no_device("misc:parser", dimension_prop_parser);
	litest_add_no_device("misc:parser", reliability_prop_parser);
	litest_add_no_device("misc:parser", calibration_prop_parser);
	litest_add_no_device("misc:parser", range_prop_parser);
	litest_add_no_device("misc:parser", palm_pressure_parser);
	litest_add_no_device("misc:parser", safe_atoi_test);
	litest_add_no_device("misc:parser", safe_atoi_base_16_test);
	litest_add_no_device("misc:parser", safe_atoi_base_8_test);
	litest_add_no_device("misc:parser", safe_atod_test);
	litest_add_no_device("misc:parser", strsplit_test);
	litest_add_no_device("misc:time", time_conversion);

	litest_add_no_device("misc:fd", fd_no_event_leak);

	litest_add_no_device("misc:library_version", library_version);
}
