1
0
forked from drath/libratbag

Add a test suite

Two components are added here: a test driver that takes a struct
ratbag_test_device and sets that device up as a ratbag device.
That device is returned to the tests and then checked for sanity.

The ratbag_device_new_test_device() requires the environment variable
RATBAG_TEST set, otherwise we abort. It's also 'hidden' in a separate source
file, so it won't show up in doxygen. And if the test are disabled, this
function does nothing and the test driver never gets registered.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer
2015-09-02 14:10:31 +10:00
committed by Benjamin Tissoires
parent 967fd3fd33
commit 28a94d0974
11 changed files with 633 additions and 6 deletions

1
.gitignore vendored
View File

@@ -44,5 +44,6 @@ test-build-pedantic-c99
test-build-std-gnuc90
test-suite.log
test-context
test-device
test-*.log
ratbag-command

View File

@@ -124,6 +124,7 @@ if test "x$build_tests" = "xyes"; then
AC_PATH_PROG(VALGRIND, [valgrind])
fi
AC_DEFINE(BUILD_TESTS, [1], ["Build the test suite])
AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"])
AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"])
AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"])

View File

@@ -7,6 +7,7 @@ libratbag_la_SOURCES = \
driver-etekcity.c \
driver-hidpp20.c \
driver-hidpp10.c \
driver-test.c \
hidpp-generic.h \
hidpp-generic.c \
hidpp10.c \
@@ -19,7 +20,9 @@ libratbag_la_SOURCES = \
libratbag-hidraw.h \
libratbag-util.c \
libratbag-private.h \
libratbag-util.h
libratbag-util.h \
libratbag-test.c \
libratbag-test.h
libratbag_la_LIBADD = $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)

105
src/driver-test.c Normal file
View File

@@ -0,0 +1,105 @@
/*
* Copyright © 2015 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 "libratbag-private.h"
#include "libratbag-test.h"
static const struct ratbag_id test_table[] = {
{.id = { .bustype = 0x00,
.vendor = 0x00,
.product = 0x00,
.version = 0x00 },
.data = 1,
},
{ },
};
static void
test_read_profile(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_test_device *d = ratbag_get_drv_data(profile->device);
struct ratbag_test_profile *p;
struct ratbag_test_resolution *r;
unsigned int i;
assert(index < d->num_profiles);
p = &d->profiles[index];
profile->resolution.num_modes = p->num_resolutions;
for (i = 0; i < p->num_resolutions; i++) {
struct ratbag_resolution *res;
r = &p->resolutions[i];
res = ratbag_resolution_init(profile, i, r->xres, r->yres, r->hz);
res->is_active = r->active;
res->is_default = r->dflt;
res->capabilities = r->caps;
res->hz = r->hz;
assert(res);
}
profile->is_active = p->active;
profile->is_default = p->dflt;
}
static int
test_probe(struct ratbag_device *device, const struct ratbag_id id)
{
struct ratbag_test_device *test_device;
assert(id.id.bustype == 0x00);
assert(id.id.vendor == 0x00);
assert(id.id.product == 0x00);
assert(id.id.version == 0x00);
assert(id.test_device != NULL);
test_device = id.test_device;
ratbag_set_drv_data(device, test_device);
ratbag_device_init_profiles(device,
test_device->num_profiles,
test_device->num_buttons);
return 0;
}
static void
test_remove(struct ratbag_device *device)
{
}
struct ratbag_driver test_driver = {
.name = "Test driver",
.table_ids = test_table,
.probe = test_probe,
.remove = test_remove,
.read_profile = test_read_profile,
.write_profile = NULL,
.set_active_profile = NULL,
.has_capability = NULL,
.read_button = NULL,
.write_button = NULL,
.write_resolution_dpi = NULL,
};

View File

@@ -37,6 +37,9 @@
#define PRODUCT_ANY 0xffff
#define VERSION_ANY 0xffff
/* This struct is used by the test suite only */
struct ratbag_test_device;
struct ratbag_driver;
struct ratbag_button_action;
@@ -77,6 +80,7 @@ struct ratbag_id {
struct input_id id;
const char *svg_filename;
unsigned long data;
struct ratbag_test_device *test_device; /**< Test suite only */
};
/**
@@ -406,7 +410,12 @@ void
ratbag_device_destroy(struct ratbag_device *device);
struct ratbag_driver *
ratbag_find_driver(struct ratbag_device *device, const struct input_id *dev_id);
ratbag_find_driver(struct ratbag_device *device,
const struct input_id *dev_id,
struct ratbag_test_device *test_device);
void
ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver);
#endif /* LIBRATBAG_PRIVATE_H */

81
src/libratbag-test.c Normal file
View File

@@ -0,0 +1,81 @@
/*
* Copyright © 2015 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 <linux/input.h>
#include <stdio.h>
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "libratbag-test.h"
struct ratbag_driver test_driver;
static inline void
ratbag_register_test_drivers(struct ratbag *ratbag)
{
struct ratbag_driver *driver;
/* Don't use a static variable here, otherwise the CK_FORK=no case
* will fail */
list_for_each(driver, &ratbag->drivers, link) {
if (streq(driver->name, test_driver.name))
return;
}
ratbag_register_driver(ratbag, &test_driver);
}
LIBRATBAG_EXPORT struct ratbag_device*
ratbag_device_new_test_device(struct ratbag *ratbag,
struct ratbag_test_device *test_device)
{
struct ratbag_device* device = NULL;
#if BUILD_TESTS
struct input_id id = {
.bustype = 0x00,
.vendor = 0x00,
.product = 0x00,
.version = 0x00,
};
ratbag_register_test_drivers(ratbag);
if (getenv("RATBAG_TEST") == NULL) {
fprintf(stderr, "RATBAG_TEST environment variable not set\n");
abort();
}
device = ratbag_device_new(ratbag, NULL, "Test device", &id);
if (!ratbag_find_driver(device, &device->ids, test_device)) {
ratbag_device_destroy(device);
return NULL;
}
#endif
return device;
}

62
src/libratbag-test.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright © 2015 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.
*/
#ifndef LIBRATBAG_TEST_H
#define LIBRATBAG_TEST_H
#include "libratbag.h"
#define RATBAG_TEST_MAX_PROFILES 12
#define RATBAG_TEST_MAX_BUTTONS 25
#define RATBAG_TEST_MAX_RESOLUTIONS 8
struct ratbag_test_button {
};
struct ratbag_test_resolution {
int xres, yres;
int hz;
bool active;
bool dflt;
uint32_t caps;
};
struct ratbag_test_profile {
unsigned int num_resolutions;
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
bool active;
bool dflt;
};
struct ratbag_test_device {
unsigned int num_profiles;
unsigned int num_buttons;
struct ratbag_test_profile profiles[RATBAG_TEST_MAX_PROFILES];
};
struct ratbag_device* ratbag_device_new_test_device(struct ratbag *ratbag,
struct ratbag_test_device *test_device);
#endif /* LIBRATBAG_TEST_H */

View File

@@ -385,7 +385,9 @@ ratbag_match_id(const struct input_id *dev_id, const struct input_id *match_id)
}
struct ratbag_driver *
ratbag_find_driver(struct ratbag_device *device, const struct input_id *dev_id)
ratbag_find_driver(struct ratbag_device *device,
const struct input_id *dev_id,
struct ratbag_test_device *test_device)
{
struct ratbag *ratbag = device->ratbag;
struct ratbag_driver *driver;
@@ -393,6 +395,8 @@ ratbag_find_driver(struct ratbag_device *device, const struct input_id *dev_id)
struct ratbag_id matched_id;
int rc;
matched_id.test_device = test_device;
list_for_each(driver, &ratbag->drivers, link) {
log_debug(ratbag, "trying driver '%s'\n", driver->name);
matching_id = driver->table_ids;
@@ -501,7 +505,7 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
if (rc)
goto out_err;
driver = ratbag_find_driver(device, &device->ids);
driver = ratbag_find_driver(device, &device->ids, NULL);
if (!driver) {
errno = ENOTSUP;
goto out_err;
@@ -550,7 +554,7 @@ ratbag_device_get_svg_name(const struct ratbag_device* device)
return device->svg_name;
}
static void
void
ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver)
{
if (!driver->name) {

View File

@@ -25,6 +25,7 @@ global:
ratbag_device_get_user_data;
ratbag_device_has_capability;
ratbag_device_new_from_udev_device;
ratbag_device_new_test_device;
ratbag_device_ref;
ratbag_device_set_user_data;
ratbag_device_unref;

View File

@@ -10,7 +10,8 @@ AM_CXXFLAGS = $(GCC_CXXFLAGS)
TEST_LIBS = $(CHECK_LIBS) $(LIBEVDEV_LIBS) $(top_builddir)/src/libratbag.la
run_tests = \
test-context
test-context \
test-device
build_tests = \
test-build-cxx \
@@ -26,6 +27,10 @@ test_context_SOURCES = test-context.c
test_context_LDADD = $(TEST_LIBS)
test_context_LDFLAGS = -no-install
test_device_SOURCES = test-device.c
test_device_LDADD = $(TEST_LIBS)
test_device_LDFLAGS = -no-install
# build-test only
test_build_pedantic_c99_SOURCES = build-pedantic.c
test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror
@@ -42,6 +47,8 @@ test_build_linker_LDADD = $(top_builddir)/src/libratbag.la
test_build_cxx_SOURCES = build-cxx.cc
test_build_cxx_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter $(AM_CXXFLAGS)
AM_TESTS_ENVIRONMENT=RATBAG_TEST=1; export RATBAG_TEST;
if HAVE_VALGRIND
VALGRIND_FLAGS=--leak-check=full \
--quiet \

353
test/test-device.c Normal file
View File

@@ -0,0 +1,353 @@
/*
* Copyright © 2015 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 <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "libratbag.h"
#include "libratbag-test.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
const struct ratbag_test_device sane_device = {
.num_profiles = 3,
.num_buttons = 1,
.profiles = {
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000 },
{ .xres = 200, .yres = 300, .hz = 1000 },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
.active = true,
.dflt = false,
},
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 1100, .yres = 1200, .hz = 2000 },
{ .xres = 1200, .yres = 1300, .hz = 2000 },
{ .xres = 1300, .yres = 1400, .hz = 2000 },
},
.active = false,
.dflt = true,
},
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 2100, .yres = 2200, .hz = 3000 },
{ .xres = 2200, .yres = 2300, .hz = 3000 },
{ .xres = 2300, .yres = 2400, .hz = 3000 },
},
.active = false,
.dflt = false,
},
},
};
static int
open_restricted(const char *path, int flags, void *user_data)
{
/* for test devices we don't expect this to be called */
ck_abort();
return 0;
}
static void
close_restricted(int fd, void *user_data)
{
/* for test devices we don't expect this to be called */
ck_abort();
}
struct ratbag_interface abort_iface = {
.open_restricted = open_restricted,
.close_restricted = close_restricted,
};
START_TEST(device_init)
{
struct ratbag *r;
struct ratbag_device *d;
int nprofiles, nbuttons;
struct ratbag_test_device td = sane_device;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
ck_assert(d != NULL);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
nbuttons = ratbag_device_get_num_buttons(d);
ck_assert_int_eq(nbuttons, 1);
ratbag_device_unref(d);
ratbag_unref(r);
}
END_TEST
START_TEST(device_profiles)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
int nprofiles;
int i;
bool is_active, is_default;
struct ratbag_test_device td = sane_device;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
for (i = 0; i < nprofiles; i++) {
p = ratbag_device_get_profile_by_index(d, i);
ck_assert(p != NULL);
is_active = ratbag_profile_is_active(p);
ck_assert_int_eq(is_active, (i == 0));
is_default = ratbag_profile_is_default(p);
ck_assert_int_eq(is_default, (i == 1));
ratbag_profile_unref(p);
}
ratbag_device_unref(d);
ratbag_unref(r);
}
END_TEST
START_TEST(device_profiles_num_0)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_test_device td = sane_device;
td.num_profiles = 0;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
ck_assert(d == NULL);
ratbag_unref(r);
}
END_TEST
START_TEST(device_profiles_multiple_active)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_test_device td = sane_device;
td.profiles[0].active = true;
td.profiles[1].active = true;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
ck_assert(d == NULL);
ratbag_unref(r);
}
END_TEST
START_TEST(device_profiles_get_invalid)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
int nprofiles;
struct ratbag_test_device td = sane_device;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile_by_index(d, nprofiles);
ck_assert(p == NULL);
p = ratbag_device_get_profile_by_index(d, nprofiles + 1);
ck_assert(p == NULL);
p = ratbag_device_get_profile_by_index(d, -1);
ck_assert(p == NULL);
p = ratbag_device_get_profile_by_index(d, INT_MAX);
ck_assert(p == NULL);
p = ratbag_device_get_profile_by_index(d, UINT_MAX);
ck_assert(p == NULL);
ratbag_device_unref(d);
ratbag_unref(r);
}
END_TEST
START_TEST(device_resolutions)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_resolution *res;
int nprofiles, nresolutions;
int i, j;
int xres, yres, rate;
struct ratbag_test_device td = {
.num_profiles = 3,
.num_buttons = 1,
.profiles = {
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000 },
{ .xres = 200, .yres = 300, .hz = 1000 },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
.active = true,
},
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 1100, .yres = 1200, .hz = 2000 },
{ .xres = 1200, .yres = 1300, .hz = 2000 },
{ .xres = 1300, .yres = 1400, .hz = 2000 },
},
},
{
.num_resolutions = 3,
.resolutions = {
{ .xres = 2100, .yres = 2200, .hz = 3000 },
{ .xres = 2200, .yres = 2300, .hz = 3000 },
{ .xres = 2300, .yres = 2400, .hz = 3000 },
},
},
},
};
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
for (i = 0; i < nprofiles; i++) {
p = ratbag_device_get_profile_by_index(d, i);
nresolutions = ratbag_profile_get_num_resolutions(p);
ck_assert_int_eq(nresolutions, 3);
for (j = 0; j < nresolutions; j++) {
res = ratbag_profile_get_resolution(p, j);
xres = ratbag_resolution_get_dpi_x(res);
yres = ratbag_resolution_get_dpi_y(res);
rate = ratbag_resolution_get_report_rate(res);
ck_assert_int_eq(xres, i * 1000 + (j + 1) * 100);
ck_assert_int_eq(yres, i * 1000 + (j + 1) * 100 + 100);
ck_assert_int_eq(xres, ratbag_resolution_get_dpi(res));
ck_assert_int_eq(rate, (i + 1) * 1000);
ratbag_resolution_unref(res);
}
ratbag_profile_unref(p);
}
ratbag_device_unref(d);
ratbag_unref(r);
}
END_TEST
START_TEST(device_resolutions_num_0)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_test_device td = {
.num_profiles = 1,
.num_buttons = 1,
.profiles = {
{
.num_resolutions = 0, /* failure trigger */
.active = true,
},
},
};
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
ck_assert(d == NULL);
ratbag_unref(r);
}
END_TEST
static Suite *
test_context_suite(void)
{
TCase *tc;
Suite *s;
s = suite_create("device");
tc = tcase_create("device");
tcase_add_test(tc, device_init);
suite_add_tcase(s, tc);
tc = tcase_create("profiles");
tcase_add_test(tc, device_profiles);
tcase_add_test(tc, device_profiles_num_0);
tcase_add_test(tc, device_profiles_multiple_active);
tcase_add_test(tc, device_profiles_get_invalid);
suite_add_tcase(s, tc);
tc = tcase_create("resolutions");
tcase_add_test(tc, device_resolutions);
tcase_add_test(tc, device_resolutions_num_0);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int nfailed;
Suite *s;
SRunner *sr;
s = test_context_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;
}