test: split out the unit tests into a separate test suite
authorPeter Hutterer <peter.hutterer@who-t.net>
Wed, 6 Mar 2019 04:19:08 +0000 (14:19 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 14 Mar 2019 01:28:05 +0000 (11:28 +1000)
All the bits that test for utility functions to work correctly can be run
separately from the main test suite (which tests devices and libinput in
general). These bits here are the ones that test the code itself and aren't
reliant on anything else.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
meson.build
test/test-misc.c
test/test-utils.c [new file with mode: 0644]

index cf41058eb854f1cc5bc7d1bb9716b9c0d464f1f5..90f0b57b980ca070b96ff0823ba9c0f3aba60df2 100644 (file)
@@ -807,6 +807,18 @@ if get_option('tests')
                                          install : false)
        test('test-library-version', test_library_version)
 
+       test_utils_sources = [
+               'src/libinput-util.h',
+               'src/libinput-util.c',
+               'test/test-utils.c',
+       ]
+       test_utils = executable('test-utils',
+                               test_utils_sources,
+                               include_directories : [includes_src, includes_include],
+                               dependencies : deps_litest,
+                               install: false)
+       test('test-utils', test_utils)
+
        libinput_test_runner_sources = litest_sources + [
                'src/libinput-util.h',
                'src/libinput-util.c',
index ce9e81902687e2634148c6fcff6fa687de66a7f7..9aa3c9e60bf0cdfb3d8966d3adbdfd74a8f83a47 100644 (file)
@@ -33,8 +33,6 @@
 
 #include "litest.h"
 #include "libinput-util.h"
-#define  TEST_VERSIONSORT
-#include "libinput-versionsort.h"
 
 static int open_restricted(const char *path, int flags, void *data)
 {
@@ -543,43 +541,6 @@ START_TEST(event_conversion_switch)
 }
 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;
@@ -620,884 +581,6 @@ START_TEST(config_status_string)
 }
 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 1000ms */
-       ratelimit_init(&rl, ms2us(1000), 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(100);
-               for (i = 0; i < 100; ++i) {
-                       ck_assert_int_eq(ratelimit_test(&rl),
-                                        RATELIMIT_EXCEEDED);
-               }
-
-               /* but after 1000ms the counter is reset */
-               msleep(950); /* +50ms 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
-
-START_TEST(dimension_prop_parser)
-{
-       struct parser_test_dimension {
-               char *tag;
-               bool success;
-               int x, y;
-       } tests[] = {
-               { "10x10", true, 10, 10 },
-               { "1x20", true, 1, 20 },
-               { "1x8000", true, 1, 8000 },
-               { "238492x428210", true, 238492, 428210 },
-               { "0x0", false, 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", false, 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
-
-START_TEST(reliability_prop_parser)
-{
-       struct parser_test_reliability {
-               char *tag;
-               bool success;
-               enum switch_reliability 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
-
-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 {
-               char *prop;
-               bool success;
-               float values[6];
-       } 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", false, DEFAULT_VALUES },
-               { 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
-
-START_TEST(range_prop_parser)
-{
-       struct parser_test_range {
-               char *tag;
-               bool success;
-               int hi, lo;
-       } 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(evcode_prop_parser)
-{
-       struct parser_test_tuple {
-               const char *prop;
-               bool success;
-               size_t ntuples;
-               int tuples[20];
-       } tests[] = {
-               { "EV_KEY", true, 1, {EV_KEY, 0xffff} },
-               { "EV_ABS;", true, 1, {EV_ABS, 0xffff} },
-               { "ABS_X;", true, 1, {EV_ABS, ABS_X} },
-               { "SW_TABLET_MODE;", true, 1, {EV_SW, SW_TABLET_MODE} },
-               { "EV_SW", true, 1, {EV_SW, 0xffff} },
-               { "ABS_Y", true, 1, {EV_ABS, ABS_Y} },
-               { "EV_ABS:0x00", true, 1, {EV_ABS, ABS_X} },
-               { "EV_ABS:01", true, 1, {EV_ABS, ABS_Y} },
-               { "ABS_TILT_X;ABS_TILT_Y;", true, 2,
-                       { EV_ABS, ABS_TILT_X,
-                         EV_ABS, ABS_TILT_Y} },
-               { "BTN_TOOL_DOUBLETAP;EV_KEY;KEY_A", true, 3,
-                       { EV_KEY, BTN_TOOL_DOUBLETAP,
-                         EV_KEY, 0xffff,
-                         EV_KEY, KEY_A } },
-               { "REL_Y;ABS_Z;BTN_STYLUS", true, 3,
-                       { EV_REL, REL_Y,
-                         EV_ABS, ABS_Z,
-                         EV_KEY, BTN_STYLUS } },
-               { "REL_Y;EV_KEY:0x123;BTN_STYLUS", true, 3,
-                       { EV_REL, REL_Y,
-                         EV_KEY, 0x123,
-                         EV_KEY, BTN_STYLUS } },
-               { .prop = "", .success = false },
-               { .prop = "EV_FOO", .success = false },
-               { .prop = "EV_KEY;EV_FOO", .success = false },
-               { .prop = "BTN_STYLUS;EV_FOO", .success = false },
-               { .prop = "BTN_UNKNOWN", .success = false },
-               { .prop = "BTN_UNKNOWN;EV_KEY", .success = false },
-               { .prop = "PR_UNKNOWN", .success = false },
-               { .prop = "BTN_STYLUS;PR_UNKNOWN;ABS_X", .success = false },
-               { .prop = "EV_REL:0xffff", .success = false },
-               { .prop = "EV_REL:0x123.", .success = false },
-               { .prop = "EV_REL:ffff", .success = false },
-               { .prop = "EV_REL:blah", .success = false },
-               { .prop = "KEY_A:0x11", .success = false },
-               { .prop = "EV_KEY:0x11 ", .success = false },
-               { .prop = "EV_KEY:0x11not", .success = false },
-               { .prop = "none", .success = false },
-               { .prop = NULL },
-       };
-       struct parser_test_tuple *t;
-
-       for (int i = 0; tests[i].prop; i++) {
-               bool success;
-               size_t nevents = 32;
-               struct input_event events[nevents];
-
-               t = &tests[i];
-               success = parse_evcode_property(t->prop, events, &nevents);
-               ck_assert(success == t->success);
-               if (!success)
-                       continue;
-
-               ck_assert_int_eq(nevents, t->ntuples);
-               for (size_t j = 0; j < nevents; j++) {
-                       int type, code;
-
-                       type = events[j].type;
-                       code = events[j].code;
-                       ck_assert_int_eq(t->tuples[j * 2], type);
-                       ck_assert_int_eq(t->tuples[j * 2 + 1], code);
-               }
-       }
-}
-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 atou_test {
-       char *str;
-       bool success;
-       unsigned int val;
-};
-
-START_TEST(safe_atou_test)
-{
-       struct atou_test tests[] = {
-               { "10", true, 10 },
-               { "20", true, 20 },
-               { "-1", false, 0 },
-               { "2147483647", true, 2147483647 },
-               { "-2147483648", 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 }
-       };
-       unsigned int v;
-       bool success;
-
-       for (int i = 0; tests[i].str != NULL; i++) {
-               v = 0xad;
-               success = safe_atou(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_atou_base_16_test)
-{
-       struct atou_test tests[] = {
-               { "10", true, 0x10 },
-               { "20", true, 0x20 },
-               { "-1", false, 0 },
-               { "0x10", true, 0x10 },
-               { "0xff", true, 0xff },
-               { "abc", true, 0xabc },
-               { "-10", false, 0 },
-               { "0x0", true, 0 },
-               { "0", true, 0 },
-               { "0x-99", false, 0 },
-               { "0xak", false, 0 },
-               { "0x", false, 0 },
-               { "x10", false, 0 },
-               { NULL, false, 0 }
-       };
-
-       unsigned int v;
-       bool success;
-
-       for (int i = 0; tests[i].str != NULL; i++) {
-               v = 0xad;
-               success = safe_atou_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_atou_base_8_test)
-{
-       struct atou_test tests[] = {
-               { "7", true, 07 },
-               { "10", true, 010 },
-               { "20", true, 020 },
-               { "-1", false, 0 },
-               { "010", true, 010 },
-               { "0ff", false, 0 },
-               { "abc", false, 0},
-               { "0xabc", false, 0},
-               { "-10", false, 0 },
-               { "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 }
-       };
-
-       unsigned int v;
-       bool success;
-
-       for (int i = 0; tests[i].str != NULL; i++) {
-               v = 0xad;
-               success = safe_atou_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
-
-START_TEST(safe_atod_test)
-{
-       struct atod_test {
-               char *str;
-               bool success;
-               double val;
-       } tests[] = {
-               { "10", true, 10 },
-               { "20", true, 20 },
-               { "-1", true, -1 },
-               { "2147483647", true, 2147483647 },
-               { "-2147483648", true, -2147483648 },
-               { "4294967295", true, 4294967295 },
-               { "0x0", false, 0 },
-               { "0x10", false, 0 },
-               { "0xaf", false, 0 },
-               { "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
-
-START_TEST(strsplit_test)
-{
-       struct strsplit_test {
-               const char *string;
-               const char *delim;
-               const char *results[10];
-       } 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
-
-START_TEST(kvsplit_double_test)
-{
-       struct kvsplit_dbl_test {
-               const char *string;
-               const char *psep;
-               const char *kvsep;
-               ssize_t nresults;
-               struct {
-                       double a;
-                       double b;
-               } results[32];
-       } tests[] = {
-               { "1:2;3:4;5:6", ";", ":", 3, { {1, 2}, {3, 4}, {5, 6}}},
-               { "1.0x2.3 -3.2x4.5 8.090909x-6.00", " ", "x", 3, { {1.0, 2.3}, {-3.2, 4.5}, {8.090909, -6}}},
-
-               { "1:2", "x", ":", 1, {{1, 2}}},
-               { "1:2", ":", "x", -1, {}},
-               { "1:2", NULL, "x", -1, {}},
-               { "1:2", "", "x", -1, {}},
-               { "1:2", "x", NULL, -1, {}},
-               { "1:2", "x", "", -1, {}},
-               { "a:b", "x", ":", -1, {}},
-               { "", " ", "x", -1, {}},
-               { "1.2.3.4.5", ".", "", -1, {}},
-               { NULL }
-       };
-       struct kvsplit_dbl_test *t = tests;
-
-       while (t->string) {
-               struct key_value_double *result = NULL;
-               ssize_t npairs;
-
-               npairs = kv_double_from_string(t->string,
-                                              t->psep,
-                                              t->kvsep,
-                                              &result);
-               ck_assert_int_eq(npairs, t->nresults);
-
-               for (ssize_t i = 0; i < npairs; i++) {
-                       ck_assert_double_eq(t->results[i].a, result[i].key);
-                       ck_assert_double_eq(t->results[i].b, result[i].value);
-               }
-
-
-               free(result);
-               t++;
-       }
-}
-END_TEST
-
-START_TEST(strjoin_test)
-{
-       struct strjoin_test {
-               char *strv[10];
-               const char *joiner;
-               const char *result;
-       } tests[] = {
-               { { "one", "two", "three", NULL }, " ", "one two three" },
-               { { "one", NULL }, "x", "one" },
-               { { "one", "two", NULL }, "x", "onextwo" },
-               { { "one", "two", NULL }, ",", "one,two" },
-               { { "one", "two", NULL }, ", ", "one, two" },
-               { { "one", "two", NULL }, "one", "oneonetwo" },
-               { { "one", "two", NULL }, NULL, NULL },
-               { { "", "", "", NULL }, " ", "  " },
-               { { "a", "b", "c", NULL }, "", "abc" },
-               { { "", "b", "c", NULL }, "x", "xbxc" },
-               { { "", "", "", NULL }, "", "" },
-               { { NULL }, NULL, NULL }
-       };
-       struct strjoin_test *t = tests;
-       struct strjoin_test nulltest = { {NULL}, "x", NULL };
-
-       while (t->strv[0]) {
-               char *str;
-               str = strv_join(t->strv, t->joiner);
-               if (t->result == NULL)
-                       ck_assert(str == NULL);
-               else
-                       ck_assert_str_eq(str, t->result);
-               free(str);
-               t++;
-       }
-
-       ck_assert(strv_join(nulltest.strv, "x") == NULL);
-}
-END_TEST
-
 static int open_restricted_leak(const char *path, int flags, void *data)
 {
        return *(int*)data;
@@ -1677,82 +760,6 @@ START_TEST(timer_flush)
 }
 END_TEST
 
-START_TEST(list_test_insert)
-{
-       struct list_test {
-               int val;
-               struct list node;
-       } tests[] = {
-               { .val  = 1 },
-               { .val  = 2 },
-               { .val  = 3 },
-               { .val  = 4 },
-       };
-       struct list_test *t;
-       struct list head;
-       int val;
-
-       list_init(&head);
-
-       ARRAY_FOR_EACH(tests, t) {
-               list_insert(&head, &t->node);
-       }
-
-       val = 4;
-       list_for_each(t, &head, node) {
-               ck_assert_int_eq(t->val, val);
-               val--;
-       }
-
-       ck_assert_int_eq(val, 0);
-}
-END_TEST
-
-START_TEST(list_test_append)
-{
-       struct list_test {
-               int val;
-               struct list node;
-       } tests[] = {
-               { .val  = 1 },
-               { .val  = 2 },
-               { .val  = 3 },
-               { .val  = 4 },
-       };
-       struct list_test *t;
-       struct list head;
-       int val;
-
-       list_init(&head);
-
-       ARRAY_FOR_EACH(tests, t) {
-               list_append(&head, &t->node);
-       }
-
-       val = 1;
-       list_for_each(t, &head, node) {
-               ck_assert_int_eq(t->val, val);
-               val++;
-       }
-       ck_assert_int_eq(val, 5);
-}
-END_TEST
-
-START_TEST(strverscmp_test)
-{
-       ck_assert_int_eq(libinput_strverscmp("", ""), 0);
-       ck_assert_int_gt(libinput_strverscmp("0.0.1", ""), 0);
-       ck_assert_int_lt(libinput_strverscmp("", "0.0.1"), 0);
-       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.0.1"), 0);
-       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.0.2"), -1);
-       ck_assert_int_eq(libinput_strverscmp("0.0.2", "0.0.1"), 1);
-       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.1.0"), -1);
-       ck_assert_int_eq(libinput_strverscmp("0.1.0", "0.0.1"), 1);
-}
-END_TEST
-
-
-
 TEST_COLLECTION(misc)
 {
        litest_add_no_device("events:conversion", event_conversion_device_notify);
@@ -1765,7 +772,6 @@ TEST_COLLECTION(misc)
        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_deviceless("misc:bitfield_helpers", bitfield_helpers);
 
        litest_add_deviceless("context:refcount", context_ref_counting);
        litest_add_deviceless("config:status string", config_status_string);
@@ -1773,31 +779,5 @@ TEST_COLLECTION(misc)
        litest_add_for_device("timer:offset-warning", timer_offset_bug_warning, LITEST_SYNAPTICS_TOUCHPAD);
        litest_add_no_device("timer:flush", timer_flush);
 
-       litest_add_deviceless("misc:matrix", matrix_helpers);
-       litest_add_deviceless("misc:ratelimit", ratelimit_helpers);
-       litest_add_deviceless("misc:parser", dpi_parser);
-       litest_add_deviceless("misc:parser", wheel_click_parser);
-       litest_add_deviceless("misc:parser", wheel_click_count_parser);
-       litest_add_deviceless("misc:parser", dimension_prop_parser);
-       litest_add_deviceless("misc:parser", reliability_prop_parser);
-       litest_add_deviceless("misc:parser", calibration_prop_parser);
-       litest_add_deviceless("misc:parser", range_prop_parser);
-       litest_add_deviceless("misc:parser", evcode_prop_parser);
-       litest_add_deviceless("misc:parser", safe_atoi_test);
-       litest_add_deviceless("misc:parser", safe_atoi_base_16_test);
-       litest_add_deviceless("misc:parser", safe_atoi_base_8_test);
-       litest_add_deviceless("misc:parser", safe_atou_test);
-       litest_add_deviceless("misc:parser", safe_atou_base_16_test);
-       litest_add_deviceless("misc:parser", safe_atou_base_8_test);
-       litest_add_deviceless("misc:parser", safe_atod_test);
-       litest_add_deviceless("misc:parser", strsplit_test);
-       litest_add_deviceless("misc:parser", kvsplit_double_test);
-       litest_add_deviceless("misc:parser", strjoin_test);
-       litest_add_deviceless("misc:time", time_conversion);
-
        litest_add_no_device("misc:fd", fd_no_event_leak);
-
-       litest_add_deviceless("misc:list", list_test_insert);
-       litest_add_deviceless("misc:list", list_test_append);
-       litest_add_deviceless("misc:versionsort", strverscmp_test);
 }
diff --git a/test/test-utils.c b/test/test-utils.c
new file mode 100644 (file)
index 0000000..eb7aa67
--- /dev/null
@@ -0,0 +1,1083 @@
+/*
+ * 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 <libinput-util.h>
+
+#include "libinput-util.h"
+#define  TEST_VERSIONSORT
+#include "libinput-versionsort.h"
+
+#include "check-double-macros.h"
+
+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(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 1000ms */
+       ratelimit_init(&rl, ms2us(1000), 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(100);
+               for (i = 0; i < 100; ++i) {
+                       ck_assert_int_eq(ratelimit_test(&rl),
+                                        RATELIMIT_EXCEEDED);
+               }
+
+               /* but after 1000ms the counter is reset */
+               msleep(950); /* +50ms 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
+
+START_TEST(dimension_prop_parser)
+{
+       struct parser_test_dimension {
+               char *tag;
+               bool success;
+               int x, y;
+       } tests[] = {
+               { "10x10", true, 10, 10 },
+               { "1x20", true, 1, 20 },
+               { "1x8000", true, 1, 8000 },
+               { "238492x428210", true, 238492, 428210 },
+               { "0x0", false, 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", false, 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
+
+START_TEST(reliability_prop_parser)
+{
+       struct parser_test_reliability {
+               char *tag;
+               bool success;
+               enum switch_reliability 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
+
+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 {
+               char *prop;
+               bool success;
+               float values[6];
+       } 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", false, DEFAULT_VALUES },
+               { 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
+
+START_TEST(range_prop_parser)
+{
+       struct parser_test_range {
+               char *tag;
+               bool success;
+               int hi, lo;
+       } 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(evcode_prop_parser)
+{
+       struct parser_test_tuple {
+               const char *prop;
+               bool success;
+               size_t ntuples;
+               int tuples[20];
+       } tests[] = {
+               { "EV_KEY", true, 1, {EV_KEY, 0xffff} },
+               { "EV_ABS;", true, 1, {EV_ABS, 0xffff} },
+               { "ABS_X;", true, 1, {EV_ABS, ABS_X} },
+               { "SW_TABLET_MODE;", true, 1, {EV_SW, SW_TABLET_MODE} },
+               { "EV_SW", true, 1, {EV_SW, 0xffff} },
+               { "ABS_Y", true, 1, {EV_ABS, ABS_Y} },
+               { "EV_ABS:0x00", true, 1, {EV_ABS, ABS_X} },
+               { "EV_ABS:01", true, 1, {EV_ABS, ABS_Y} },
+               { "ABS_TILT_X;ABS_TILT_Y;", true, 2,
+                       { EV_ABS, ABS_TILT_X,
+                         EV_ABS, ABS_TILT_Y} },
+               { "BTN_TOOL_DOUBLETAP;EV_KEY;KEY_A", true, 3,
+                       { EV_KEY, BTN_TOOL_DOUBLETAP,
+                         EV_KEY, 0xffff,
+                         EV_KEY, KEY_A } },
+               { "REL_Y;ABS_Z;BTN_STYLUS", true, 3,
+                       { EV_REL, REL_Y,
+                         EV_ABS, ABS_Z,
+                         EV_KEY, BTN_STYLUS } },
+               { "REL_Y;EV_KEY:0x123;BTN_STYLUS", true, 3,
+                       { EV_REL, REL_Y,
+                         EV_KEY, 0x123,
+                         EV_KEY, BTN_STYLUS } },
+               { .prop = "", .success = false },
+               { .prop = "EV_FOO", .success = false },
+               { .prop = "EV_KEY;EV_FOO", .success = false },
+               { .prop = "BTN_STYLUS;EV_FOO", .success = false },
+               { .prop = "BTN_UNKNOWN", .success = false },
+               { .prop = "BTN_UNKNOWN;EV_KEY", .success = false },
+               { .prop = "PR_UNKNOWN", .success = false },
+               { .prop = "BTN_STYLUS;PR_UNKNOWN;ABS_X", .success = false },
+               { .prop = "EV_REL:0xffff", .success = false },
+               { .prop = "EV_REL:0x123.", .success = false },
+               { .prop = "EV_REL:ffff", .success = false },
+               { .prop = "EV_REL:blah", .success = false },
+               { .prop = "KEY_A:0x11", .success = false },
+               { .prop = "EV_KEY:0x11 ", .success = false },
+               { .prop = "EV_KEY:0x11not", .success = false },
+               { .prop = "none", .success = false },
+               { .prop = NULL },
+       };
+       struct parser_test_tuple *t;
+
+       for (int i = 0; tests[i].prop; i++) {
+               bool success;
+               size_t nevents = 32;
+               struct input_event events[nevents];
+
+               t = &tests[i];
+               success = parse_evcode_property(t->prop, events, &nevents);
+               ck_assert(success == t->success);
+               if (!success)
+                       continue;
+
+               ck_assert_int_eq(nevents, t->ntuples);
+               for (size_t j = 0; j < nevents; j++) {
+                       int type, code;
+
+                       type = events[j].type;
+                       code = events[j].code;
+                       ck_assert_int_eq(t->tuples[j * 2], type);
+                       ck_assert_int_eq(t->tuples[j * 2 + 1], code);
+               }
+       }
+}
+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 atou_test {
+       char *str;
+       bool success;
+       unsigned int val;
+};
+
+START_TEST(safe_atou_test)
+{
+       struct atou_test tests[] = {
+               { "10", true, 10 },
+               { "20", true, 20 },
+               { "-1", false, 0 },
+               { "2147483647", true, 2147483647 },
+               { "-2147483648", 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 }
+       };
+       unsigned int v;
+       bool success;
+
+       for (int i = 0; tests[i].str != NULL; i++) {
+               v = 0xad;
+               success = safe_atou(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_atou_base_16_test)
+{
+       struct atou_test tests[] = {
+               { "10", true, 0x10 },
+               { "20", true, 0x20 },
+               { "-1", false, 0 },
+               { "0x10", true, 0x10 },
+               { "0xff", true, 0xff },
+               { "abc", true, 0xabc },
+               { "-10", false, 0 },
+               { "0x0", true, 0 },
+               { "0", true, 0 },
+               { "0x-99", false, 0 },
+               { "0xak", false, 0 },
+               { "0x", false, 0 },
+               { "x10", false, 0 },
+               { NULL, false, 0 }
+       };
+
+       unsigned int v;
+       bool success;
+
+       for (int i = 0; tests[i].str != NULL; i++) {
+               v = 0xad;
+               success = safe_atou_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_atou_base_8_test)
+{
+       struct atou_test tests[] = {
+               { "7", true, 07 },
+               { "10", true, 010 },
+               { "20", true, 020 },
+               { "-1", false, 0 },
+               { "010", true, 010 },
+               { "0ff", false, 0 },
+               { "abc", false, 0},
+               { "0xabc", false, 0},
+               { "-10", false, 0 },
+               { "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 }
+       };
+
+       unsigned int v;
+       bool success;
+
+       for (int i = 0; tests[i].str != NULL; i++) {
+               v = 0xad;
+               success = safe_atou_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
+
+START_TEST(safe_atod_test)
+{
+       struct atod_test {
+               char *str;
+               bool success;
+               double val;
+       } tests[] = {
+               { "10", true, 10 },
+               { "20", true, 20 },
+               { "-1", true, -1 },
+               { "2147483647", true, 2147483647 },
+               { "-2147483648", true, -2147483648 },
+               { "4294967295", true, 4294967295 },
+               { "0x0", false, 0 },
+               { "0x10", false, 0 },
+               { "0xaf", false, 0 },
+               { "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
+
+START_TEST(strsplit_test)
+{
+       struct strsplit_test {
+               const char *string;
+               const char *delim;
+               const char *results[10];
+       } 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
+
+START_TEST(kvsplit_double_test)
+{
+       struct kvsplit_dbl_test {
+               const char *string;
+               const char *psep;
+               const char *kvsep;
+               ssize_t nresults;
+               struct {
+                       double a;
+                       double b;
+               } results[32];
+       } tests[] = {
+               { "1:2;3:4;5:6", ";", ":", 3, { {1, 2}, {3, 4}, {5, 6}}},
+               { "1.0x2.3 -3.2x4.5 8.090909x-6.00", " ", "x", 3, { {1.0, 2.3}, {-3.2, 4.5}, {8.090909, -6}}},
+
+               { "1:2", "x", ":", 1, {{1, 2}}},
+               { "1:2", ":", "x", -1, {}},
+               { "1:2", NULL, "x", -1, {}},
+               { "1:2", "", "x", -1, {}},
+               { "1:2", "x", NULL, -1, {}},
+               { "1:2", "x", "", -1, {}},
+               { "a:b", "x", ":", -1, {}},
+               { "", " ", "x", -1, {}},
+               { "1.2.3.4.5", ".", "", -1, {}},
+               { NULL }
+       };
+       struct kvsplit_dbl_test *t = tests;
+
+       while (t->string) {
+               struct key_value_double *result = NULL;
+               ssize_t npairs;
+
+               npairs = kv_double_from_string(t->string,
+                                              t->psep,
+                                              t->kvsep,
+                                              &result);
+               ck_assert_int_eq(npairs, t->nresults);
+
+               for (ssize_t i = 0; i < npairs; i++) {
+                       ck_assert_double_eq(t->results[i].a, result[i].key);
+                       ck_assert_double_eq(t->results[i].b, result[i].value);
+               }
+
+
+               free(result);
+               t++;
+       }
+}
+END_TEST
+
+START_TEST(strjoin_test)
+{
+       struct strjoin_test {
+               char *strv[10];
+               const char *joiner;
+               const char *result;
+       } tests[] = {
+               { { "one", "two", "three", NULL }, " ", "one two three" },
+               { { "one", NULL }, "x", "one" },
+               { { "one", "two", NULL }, "x", "onextwo" },
+               { { "one", "two", NULL }, ",", "one,two" },
+               { { "one", "two", NULL }, ", ", "one, two" },
+               { { "one", "two", NULL }, "one", "oneonetwo" },
+               { { "one", "two", NULL }, NULL, NULL },
+               { { "", "", "", NULL }, " ", "  " },
+               { { "a", "b", "c", NULL }, "", "abc" },
+               { { "", "b", "c", NULL }, "x", "xbxc" },
+               { { "", "", "", NULL }, "", "" },
+               { { NULL }, NULL, NULL }
+       };
+       struct strjoin_test *t = tests;
+       struct strjoin_test nulltest = { {NULL}, "x", NULL };
+
+       while (t->strv[0]) {
+               char *str;
+               str = strv_join(t->strv, t->joiner);
+               if (t->result == NULL)
+                       ck_assert(str == NULL);
+               else
+                       ck_assert_str_eq(str, t->result);
+               free(str);
+               t++;
+       }
+
+       ck_assert(strv_join(nulltest.strv, "x") == NULL);
+}
+END_TEST
+
+START_TEST(list_test_insert)
+{
+       struct list_test {
+               int val;
+               struct list node;
+       } tests[] = {
+               { .val  = 1 },
+               { .val  = 2 },
+               { .val  = 3 },
+               { .val  = 4 },
+       };
+       struct list_test *t;
+       struct list head;
+       int val;
+
+       list_init(&head);
+
+       ARRAY_FOR_EACH(tests, t) {
+               list_insert(&head, &t->node);
+       }
+
+       val = 4;
+       list_for_each(t, &head, node) {
+               ck_assert_int_eq(t->val, val);
+               val--;
+       }
+
+       ck_assert_int_eq(val, 0);
+}
+END_TEST
+
+START_TEST(list_test_append)
+{
+       struct list_test {
+               int val;
+               struct list node;
+       } tests[] = {
+               { .val  = 1 },
+               { .val  = 2 },
+               { .val  = 3 },
+               { .val  = 4 },
+       };
+       struct list_test *t;
+       struct list head;
+       int val;
+
+       list_init(&head);
+
+       ARRAY_FOR_EACH(tests, t) {
+               list_append(&head, &t->node);
+       }
+
+       val = 1;
+       list_for_each(t, &head, node) {
+               ck_assert_int_eq(t->val, val);
+               val++;
+       }
+       ck_assert_int_eq(val, 5);
+}
+END_TEST
+
+START_TEST(strverscmp_test)
+{
+       ck_assert_int_eq(libinput_strverscmp("", ""), 0);
+       ck_assert_int_gt(libinput_strverscmp("0.0.1", ""), 0);
+       ck_assert_int_lt(libinput_strverscmp("", "0.0.1"), 0);
+       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.0.1"), 0);
+       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.0.2"), -1);
+       ck_assert_int_eq(libinput_strverscmp("0.0.2", "0.0.1"), 1);
+       ck_assert_int_eq(libinput_strverscmp("0.0.1", "0.1.0"), -1);
+       ck_assert_int_eq(libinput_strverscmp("0.1.0", "0.0.1"), 1);
+}
+END_TEST
+
+static Suite *
+litest_utils_suite(void)
+{
+       TCase *tc;
+       Suite *s;
+
+       s = suite_create("litest:utils");
+       tc = tcase_create("utils");
+
+       tcase_add_test(tc, bitfield_helpers);
+       tcase_add_test(tc, matrix_helpers);
+       tcase_add_test(tc, ratelimit_helpers);
+       tcase_add_test(tc, dpi_parser);
+       tcase_add_test(tc, wheel_click_parser);
+       tcase_add_test(tc, wheel_click_count_parser);
+       tcase_add_test(tc, dimension_prop_parser);
+       tcase_add_test(tc, reliability_prop_parser);
+       tcase_add_test(tc, calibration_prop_parser);
+       tcase_add_test(tc, range_prop_parser);
+       tcase_add_test(tc, evcode_prop_parser);
+       tcase_add_test(tc, safe_atoi_test);
+       tcase_add_test(tc, safe_atoi_base_16_test);
+       tcase_add_test(tc, safe_atoi_base_8_test);
+       tcase_add_test(tc, safe_atou_test);
+       tcase_add_test(tc, safe_atou_base_16_test);
+       tcase_add_test(tc, safe_atou_base_8_test);
+       tcase_add_test(tc, safe_atod_test);
+       tcase_add_test(tc, strsplit_test);
+       tcase_add_test(tc, kvsplit_double_test);
+       tcase_add_test(tc, strjoin_test);
+       tcase_add_test(tc, time_conversion);
+
+       tcase_add_test(tc, list_test_insert);
+       tcase_add_test(tc, list_test_append);
+       tcase_add_test(tc, strverscmp_test);
+
+       return s;
+}
+
+int main(int argc, char **argv)
+{
+       int nfailed;
+       Suite *s;
+       SRunner *sr;
+
+        /* when running under valgrind we're using nofork mode, so a signal
+         * raised by a test will fail in valgrind. There's nothing to
+         * memcheck here anyway, so just skip the valgrind test */
+        if (getenv("USING_VALGRIND"))
+            return EXIT_SUCCESS;
+
+       s = litest_utils_suite();
+        sr = srunner_create(s);
+
+       srunner_run_all(sr, CK_ENV);
+       nfailed = srunner_ntests_failed(sr);
+       srunner_free(sr);
+
+       return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}