libinput/test/test-quirks.c

1483 lines
34 KiB
C
Raw Permalink Normal View History

/*
* Copyright © 2018 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.h>
#include "libinput-util.h"
#include "litest.h"
#include "quirks.h"
static void
log_handler(struct libinput *this_is_null,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
#if 0
vprintf(format, args);
#endif
}
struct data_dir {
char *dirname;
char *filename;
};
static struct data_dir
make_data_dir(const char *file_content)
{
struct data_dir dir = {0};
char dirname[PATH_MAX] = "/tmp/litest-quirk-test-XXXXXX";
char *filename;
FILE *fp;
int rc;
litest_assert_notnull(mkdtemp(dirname));
dir.dirname = safe_strdup(dirname);
if (file_content) {
rc = xasprintf(&filename, "%s/testfile.quirks", dirname);
litest_assert_int_eq(rc, (int)(strlen(dirname) + 16));
fp = fopen(filename, "w+");
litest_assert_notnull(fp);
rc = fputs(file_content, fp);
fclose(fp);
litest_assert_int_ge(rc, 0);
dir.filename = filename;
}
return dir;
}
static void
cleanup_data_dir(struct data_dir dd)
{
if (dd.filename) {
unlink(dd.filename);
free(dd.filename);
}
if (dd.dirname) {
rmdir(dd.dirname);
free(dd.dirname);
}
}
START_TEST(quirks_invalid_dir)
{
struct quirks_context *ctx;
ctx = quirks_init_subsystem("/does-not-exist",
NULL,
log_handler,
NULL,
QLOG_LIBINPUT_LOGGING);
ck_assert(ctx == NULL);
}
END_TEST
START_TEST(quirks_empty_dir)
{
struct quirks_context *ctx;
struct data_dir dd = make_data_dir(NULL);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_LIBINPUT_LOGGING);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_empty)
{
struct quirks_context *ctx;
const char quirks_file[] = "[Empty Section]";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_double)
{
struct quirks_context *ctx;
const char quirks_file[] = "[Section name]";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_missing_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_missing_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_match_after_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n"
"MatchName=mouse\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_duplicate_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_duplicate_attr)
{
/* This shouldn't be allowed but the current parser
is happy with it */
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_section)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section Missing Bracket\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_trailing_whitespace)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse \n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"Matchblahblah=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"Attrblahblah=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_model)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"Modelblahblah=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_prefix)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"Fooblahblah=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_model_not_one)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=true\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_comment_inline)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name] # some inline comment\n"
"MatchUdevType=mouse\t # another inline comment\n"
"ModelAppleTouchpad=1#\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_comment_empty)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"#\n"
" #\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_string_quotes_single)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrKeyboardIntegration='internal'\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_string_quotes_double)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrKeyboardIntegration=\"internal\"\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_bustype)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchBus=usb\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=bluetooth\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=i2c\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=rmi\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=ps2\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_bustype_invalid)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchBus=venga\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_vendor)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchVendor=0x0000\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVendor=0x0001\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVendor=0x2343\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_vendor_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchVendor=-1\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=abc\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=0xFFFFF\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_product)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchProduct=0x0000\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x0001\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x2343\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_product_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchProduct=-1\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=abc\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=0xFFFFF\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_version)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchVersion=0x0000\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVersion=0x0001\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVersion=0x2343\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_version_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchVersion=-1\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVersion=abc\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVersion=0xFFFFF\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVersion=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_name)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchName=1235\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=abc\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=*foo\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=foo*\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=foo[]\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=*foo*\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_name_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchName=\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_udev)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=touchpad\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=pointingstick\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=tablet\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=tablet-pad\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=keyboard\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=joystick\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_udev_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchUdevType=blah\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchUdevType=\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchUdevType=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_dmi)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchDMIModalias=dmi:*\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchDMIModalias=dmi:*svn*pn*:\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_dmi_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchDMIModalias=\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=*pn*\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=dmi*pn*\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=foo\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
typedef bool (*qparsefunc) (struct quirks *q, enum quirk which, void* data);
/*
Helper for generic testing, matches on a mouse device with the given
quirk set to the given string. Creates a data directory, inits the quirks
and calls func() to return the value in data. The func has to take the
right data, otherwise boom. Usage:
rc = test_attr_parse(dev, QUIRK_ATTR_SIZE_HINT,
"10x30", quirks_get_dimensions,
&some_struct_quirks_dimensions);
if (rc == false) // failed to parse
else // struct now contains the 10, 30 values
*/
static bool
test_attr_parse(struct litest_device *dev,
enum quirk which,
const char *str,
qparsefunc func,
void *data)
{
struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
struct quirks_context *ctx;
struct data_dir dd;
char buf[512];
bool result;
snprintf(buf,
sizeof(buf),
"[Section name]\n"
"MatchUdevType=mouse\n"
"%s=%s\n",
quirk_get_name(which),
str);
dd = make_data_dir(buf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
if (ctx != NULL) {
struct quirks *q;
q = quirks_fetch_for_device(ctx, ud);
ck_assert_notnull(q);
ck_assert(func(q, which, data));
ck_assert(quirks_has_quirk(q, which));
quirks_unref(q);
quirks_context_unref(ctx);
result = true;
} else {
result = false;
}
cleanup_data_dir(dd);
udev_device_unref(ud);
return result;
}
struct qtest_dim {
const char *str;
bool success;
int w, h;
};
START_TEST(quirks_parse_dimension_attr)
{
struct litest_device *dev = litest_current_device();
enum quirk attrs[] = {
QUIRK_ATTR_SIZE_HINT,
QUIRK_ATTR_RESOLUTION_HINT,
};
enum quirk *a;
struct qtest_dim test_values[] = {
{ "10x10", true, 10, 10 },
{ "20x30", true, 20, 30 },
{ "-10x30", false, 0, 0 },
{ "10:30", false, 0, 0 },
{ "30", false, 0, 0 },
{ "0x00", false, 0, 0 },
{ "0xa0", false, 0, 0 },
};
struct qtest_dim *t;
ARRAY_FOR_EACH(attrs, a) {
ARRAY_FOR_EACH(test_values, t) {
struct quirk_dimensions dim;
bool rc;
rc = test_attr_parse(dev,
*a,
t->str,
(qparsefunc)quirks_get_dimensions,
&dim);
ck_assert_int_eq(rc, t->success);
if (!rc)
continue;
ck_assert_int_eq(dim.x, t->w);
ck_assert_int_eq(dim.y, t->h);
}
}
}
END_TEST
struct qtest_range {
const char *str;
bool success;
int hi, lo;
};
START_TEST(quirks_parse_range_attr)
{
struct litest_device *dev = litest_current_device();
enum quirk attrs[] = {
QUIRK_ATTR_TOUCH_SIZE_RANGE,
QUIRK_ATTR_PRESSURE_RANGE,
};
enum quirk *a;
struct qtest_range test_values[] = {
{ "20:10", true, 20, 10 },
{ "30:5", true, 30, 5 },
{ "30:-10", true, 30, -10 },
{ "-30:-100", true, -30, -100 },
{ "5:10", false, 0, 0 },
{ "5:5", false, 0, 0 },
{ "-10:5", false, 0, 0 },
{ "-10:-5", false, 0, 0 },
{ "10x30", false, 0, 0 },
{ "30x10", false, 0, 0 },
{ "30", false, 0, 0 },
{ "0x00", false, 0, 0 },
{ "0xa0", false, 0, 0 },
{ "0x10:0x5", false, 0, 0 },
};
struct qtest_range *t;
ARRAY_FOR_EACH(attrs, a) {
ARRAY_FOR_EACH(test_values, t) {
struct quirk_range r;
bool rc;
rc = test_attr_parse(dev,
*a,
t->str,
(qparsefunc)quirks_get_range,
&r);
ck_assert_int_eq(rc, t->success);
if (!rc)
continue;
ck_assert_int_eq(r.lower, t->lo);
ck_assert_int_eq(r.upper, t->hi);
}
}
}
END_TEST
struct qtest_uint {
const char *str;
bool success;
uint32_t val;
};
START_TEST(quirks_parse_uint_attr)
{
struct litest_device *dev = litest_current_device();
enum quirk attrs[] = {
QUIRK_ATTR_PALM_SIZE_THRESHOLD,
QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
};
enum quirk *a;
struct qtest_uint test_values[] = {
{ "10", true, 10 },
{ "0", true, 0 },
{ "5", true, 5 },
{ "65535", true, 65535 },
{ "4294967295", true, 4294967295 },
{ "-10", false, 0 },
{ "0x10", false, 0 },
{ "0xab", false, 0 },
{ "ab", false, 0 },
};
struct qtest_uint *t;
ARRAY_FOR_EACH(attrs, a) {
ARRAY_FOR_EACH(test_values, t) {
uint32_t v;
bool rc;
rc = test_attr_parse(dev,
*a,
t->str,
(qparsefunc)quirks_get_uint32,
&v);
ck_assert_int_eq(rc, t->success);
if (!rc)
continue;
ck_assert_int_eq(v, t->val);
}
}
}
END_TEST
struct qtest_double {
const char *str;
bool success;
double val;
};
START_TEST(quirks_parse_double_attr)
{
struct litest_device *dev = litest_current_device();
enum quirk attrs[] = {
QUIRK_ATTR_TRACKPOINT_MULTIPLIER,
};
enum quirk *a;
struct qtest_double test_values[] = {
{ "10", true, 10.0 },
{ "10.0", true, 10.0 },
{ "-10.0", true, -10.0 },
{ "0", true, 0.0 },
{ "0.0", true, 0.0 },
{ "5.1", true, 5.1 },
{ "-5.9", true, -5.9 },
{ "65535", true, 65535 },
{ "4294967295", true, 4294967295 },
{ "4294967295.123", true, 4294967295.123 },
/* our safe_atoi parses hex even though we don't really want
* to */
{ "0x10", false, 0 },
{ "0xab", false, 0 },
{ "ab", false, 0 },
{ "10:5", false, 0 },
{ "10x5", false, 0 },
};
struct qtest_double *t;
ARRAY_FOR_EACH(attrs, a) {
ARRAY_FOR_EACH(test_values, t) {
double v;
bool rc;
rc = test_attr_parse(dev,
*a,
t->str,
(qparsefunc)quirks_get_double,
&v);
ck_assert_int_eq(rc, t->success);
if (!rc)
continue;
ck_assert_int_eq(v, t->val);
}
}
}
END_TEST
struct qtest_str {
const char *str;
enum quirk where;
};
START_TEST(quirks_parse_string_attr)
{
struct litest_device *dev = litest_current_device();
enum quirk attrs[] = {
QUIRK_ATTR_TPKBCOMBO_LAYOUT,
QUIRK_ATTR_LID_SWITCH_RELIABILITY,
QUIRK_ATTR_KEYBOARD_INTEGRATION,
};
enum quirk *a;
struct qtest_str test_values[] = {
{ "below", QUIRK_ATTR_TPKBCOMBO_LAYOUT },
{ "reliable", QUIRK_ATTR_LID_SWITCH_RELIABILITY },
{ "write_open", QUIRK_ATTR_LID_SWITCH_RELIABILITY },
{ "internal", QUIRK_ATTR_KEYBOARD_INTEGRATION },
{ "external", QUIRK_ATTR_KEYBOARD_INTEGRATION },
{ "10", 0 },
{ "-10", 0 },
{ "0", 0 },
{ "", 0 },
{ "banana", 0 },
{ "honk honk", 0 },
{ "0x12", 0 },
{ "0xa", 0 },
{ "0.0", 0 },
};
struct qtest_str *t;
ARRAY_FOR_EACH(attrs, a) {
ARRAY_FOR_EACH(test_values, t) {
bool rc;
char *do_not_use; /* freed before we can use it */
rc = test_attr_parse(dev,
*a,
t->str,
(qparsefunc)quirks_get_string,
&do_not_use);
if (*a == t->where)
ck_assert_int_eq(rc, true);
else
ck_assert_int_eq(rc, false);
}
}
}
END_TEST
START_TEST(quirks_parse_integration_attr)
{
struct litest_device *dev = litest_current_device();
char *do_not_use; /* freed before we can use it */
bool
rc = test_attr_parse(dev,
QUIRK_ATTR_KEYBOARD_INTEGRATION,
"internal",
(qparsefunc)quirks_get_string,
&do_not_use);
ck_assert(rc);
rc = test_attr_parse(dev,
QUIRK_ATTR_KEYBOARD_INTEGRATION,
"external",
(qparsefunc)quirks_get_string,
&do_not_use);
ck_assert(rc);
rc = test_attr_parse(dev,
QUIRK_ATTR_TRACKPOINT_INTEGRATION,
"internal",
(qparsefunc)quirks_get_string,
&do_not_use);
ck_assert(rc);
rc = test_attr_parse(dev,
QUIRK_ATTR_TRACKPOINT_INTEGRATION,
"external",
(qparsefunc)quirks_get_string,
&do_not_use);
ck_assert(rc);
}
END_TEST
START_TEST(quirks_model_one)
{
struct litest_device *dev = litest_current_device();
struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
struct quirks *q;
bool isset;
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
q = quirks_fetch_for_device(ctx, ud);
ck_assert_notnull(q);
ck_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
ck_assert(isset == true);
quirks_unref(q);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
udev_device_unref(ud);
}
END_TEST
START_TEST(quirks_model_zero)
{
struct litest_device *dev = litest_current_device();
struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=0\n";
struct data_dir dd = make_data_dir(quirks_file);
struct quirks *q;
bool isset;
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
q = quirks_fetch_for_device(ctx, ud);
ck_assert_notnull(q);
ck_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
ck_assert(isset == false);
quirks_unref(q);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
udev_device_unref(ud);
}
END_TEST
START_TEST(quirks_model_alps)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct quirks *q;
bool exists, value = false;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD, &value);
if (strstr(libinput_device_get_name(device), "ALPS")) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_wacom)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value = false;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_WACOM_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_WACOM) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_apple)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value = false;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_APPLE) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_synaptics_serial)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value = false;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_SYNAPTICS_SERIAL &&
libevdev_get_id_product(dev->evdev) == PRODUCT_ID_SYNAPTICS_SERIAL) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_call_NULL)
{
ck_assert(!quirks_fetch_for_device(NULL, NULL));
ck_assert(!quirks_get_uint32(NULL, 0, NULL));
ck_assert(!quirks_get_int32(NULL, 0, NULL));
ck_assert(!quirks_get_range(NULL, 0, NULL));
ck_assert(!quirks_get_dimensions(NULL, 0, NULL));
ck_assert(!quirks_get_double(NULL, 0, NULL));
ck_assert(!quirks_get_string(NULL, 0, NULL));
ck_assert(!quirks_get_bool(NULL, 0, NULL));
}
END_TEST
START_TEST(quirks_ctx_ref)
{
struct quirks_context *ctx, *ctx2;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
ctx2 = quirks_context_ref(ctx);
litest_assert_ptr_eq(ctx, ctx2);
ctx2 = quirks_context_unref(ctx);
litest_assert_ptr_eq(ctx2, NULL);
ctx2 = quirks_context_unref(ctx);
litest_assert_ptr_eq(ctx2, NULL);
cleanup_data_dir(dd);
}
END_TEST
TEST_COLLECTION(quirks)
{
litest_add_deviceless("quirks:datadir", quirks_invalid_dir);
litest_add_deviceless("quirks:datadir", quirks_empty_dir);
litest_add_deviceless("quirks:structure", quirks_section_empty);
litest_add_deviceless("quirks:structure", quirks_section_double);
litest_add_deviceless("quirks:structure", quirks_section_missing_match);
litest_add_deviceless("quirks:structure", quirks_section_missing_attr);
litest_add_deviceless("quirks:structure", quirks_section_match_after_attr);
litest_add_deviceless("quirks:structure", quirks_section_duplicate_match);
litest_add_deviceless("quirks:structure", quirks_section_duplicate_attr);
litest_add_deviceless("quirks:parsing", quirks_parse_error_section);
litest_add_deviceless("quirks:parsing", quirks_parse_error_trailing_whitespace);
litest_add_deviceless("quirks:parsing", quirks_parse_error_unknown_match);
litest_add_deviceless("quirks:parsing", quirks_parse_error_unknown_attr);
litest_add_deviceless("quirks:parsing", quirks_parse_error_unknown_model);
litest_add_deviceless("quirks:parsing", quirks_parse_error_unknown_prefix);
litest_add_deviceless("quirks:parsing", quirks_parse_error_model_not_one);
litest_add_deviceless("quirks:parsing", quirks_parse_comment_inline);
litest_add_deviceless("quirks:parsing", quirks_parse_comment_empty);
litest_add_deviceless("quirks:parsing", quirks_parse_string_quotes_single);
litest_add_deviceless("quirks:parsing", quirks_parse_string_quotes_double);
litest_add_deviceless("quirks:parsing", quirks_parse_bustype);
litest_add_deviceless("quirks:parsing", quirks_parse_bustype_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_vendor);
litest_add_deviceless("quirks:parsing", quirks_parse_vendor_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_product);
litest_add_deviceless("quirks:parsing", quirks_parse_product_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_version);
litest_add_deviceless("quirks:parsing", quirks_parse_version_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_name);
litest_add_deviceless("quirks:parsing", quirks_parse_name_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_udev);
litest_add_deviceless("quirks:parsing", quirks_parse_udev_invalid);
litest_add_deviceless("quirks:parsing", quirks_parse_dmi);
litest_add_deviceless("quirks:parsing", quirks_parse_dmi_invalid);
litest_add_for_device("quirks:parsing", quirks_parse_dimension_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_range_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_uint_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_double_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_string_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_integration_attr, LITEST_MOUSE);
litest_add_for_device("quirks:model", quirks_model_one, LITEST_MOUSE);
litest_add_for_device("quirks:model", quirks_model_zero, LITEST_MOUSE);
litest_add("quirks:devices", quirks_model_alps, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_wacom, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_apple, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY);
litest_add_deviceless("quirks:misc", quirks_call_NULL);
litest_add_deviceless("quirks:misc", quirks_ctx_ref);
}