Remove manual tests in favor of cmocka

Prior to this change, we needed to add a manual override function to
mock the vendor command. Given that cmocka has built-in support for
mocking functions, it's better to use that instead.

This change simply removes the manual override and any tests that rely
on it.
pull/22/head
nirenjan 2020-06-16 17:30:11 -07:00
parent f0ed2f39e3
commit bf9b1bdfbd
10 changed files with 17 additions and 531 deletions

View File

@ -62,6 +62,12 @@ AM_COND_IF([HAVE_DOXYGEN],
[AC_CONFIG_FILES([Doxyfile])],
[AC_MSG_WARN(["Doxygen not found; continuing without doxygen support"])])
# cmocka unit tests
AX_PKG_CHECK_MODULES([CMOCKA], [cmocka], [], [have_cmocka=yes], [have_cmocka=no])
AM_CONDITIONAL([HAVE_CMOCKA], [test "x$have_cmocka" = xyes])
AM_COND_IF([HAVE_CMOCKA], [],
[AC_MSG_WARN(["cmocka not found; disabling unit test build"])])
# Check for the presence of tm_gmtoff in struct tm. If we have this, then we
# can use it to determine the true GMT offset
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,

View File

@ -31,25 +31,12 @@ x52include_HEADERS = libx52.h
# pkg-config files
pkgconfig_DATA = libx52.pc
check_PROGRAMS = test_offset test_led test_blink_shift
test_offset_SOURCES = test_offset.c test_common.c
test_offset_CFLAGS = @LIBUSB_CFLAGS@
test_offset_LDADD = libx52.la
test_led_SOURCES = test_led.c test_common.c
test_led_CFLAGS = @LIBUSB_CFLAGS@
test_led_LDADD = libx52.la
test_blink_shift_SOURCES = test_blink_shift.c test_common.c
test_blink_shift_CFLAGS = @LIBUSB_CFLAGS@
test_blink_shift_LDADD = libx52.la
if HAVE_CMOCKA
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
TESTS = test_offset test_led test_blink_shift
endif
# Extra files that need to be in the distribution
EXTRA_DIST = libx52.h x52_commands.h x52_common.h test_common.h README.md
EXTRA_DIST = libx52.h x52_commands.h x52_common.h README.md
# Add documentation files to the distribution
EXTRA_DIST += \

View File

@ -1,59 +0,0 @@
/*
* Saitek X52 Pro MFD & LED driver
* Test program for validating blink and shift functionality
*
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "x52_common.h"
#include "test_common.h"
#include "x52_commands.h"
typedef int (*blink_shift_fn)(libx52_device *, uint8_t);
TEST_STRUCT (
blink_shift_fn func;
uint8_t state;
struct ivpair data[2];
)
#define on 1
#define off 0
#define x52_shift_indicator 0xfd
#define x52_shift_on 0x51
#define x52_shift_off 0x50
#define x52_blink_indicator 0xb4
#define x52_blink_on 0x51
#define x52_blink_off 0x50
#define TEST(func, state) { #func "/" #state, libx52_set_ ## func, state, {{ x52_ ## func ## _indicator, x52_ ## func ## _ ## state}, {0, 0}}}
TEST_CASES = {
TEST(blink, on),
TEST(blink, off),
TEST(shift, on),
TEST(shift, off),
};
TEST_FUNC()
{
TEST_INIT();
int rc = (*test.func)(dev, test.state);
if (rc != LIBX52_SUCCESS) {
PRINT_FAIL();
printf("# Expected success, got %d\n", rc);
return;
}
TEST_VERIFY(test.data);
}
TEST_MAIN()

View File

@ -1,155 +0,0 @@
/*
* Saitek X52 Pro MFD & LED driver
* Common functionality for test programs
*
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "x52_common.h"
#include "test_common.h"
#define MAX_DIAGS 32
#define MAX_DIAG_SZ 256
static int diag_count;
static char diagnostic[MAX_DIAGS][MAX_DIAG_SZ];
#define ADD_DIAG(fmt_str, ...) do { \
if (diag_count < MAX_DIAGS) { \
snprintf(diagnostic[diag_count], MAX_DIAG_SZ, fmt_str, ##__VA_ARGS__); \
diag_count++; \
} \
} while(0)
/* Test vendor command function */
int x52_test_vendor_command(libx52_device *dev, uint16_t index, uint16_t value)
{
struct x52_vendor_data *vdata = (struct x52_vendor_data *)dev->hdl;
struct ivpair data = {index, value};
if (vdata->written < MAX_SZ) {
vdata->data[vdata->written] = data;
vdata->written++;
}
return LIBX52_SUCCESS;
}
/* Check expected data */
bool x52_test_assert_expected(libx52_device *dev, struct ivpair *data)
{
int written = 0;
struct x52_vendor_data *vdata = (struct x52_vendor_data *)dev->hdl;
while((data[written].index != 0 || data[written].value != 0) && written < vdata->written) {
if ((data[written].index != vdata->data[written].index) ||
(data[written].value != vdata->data[written].value)) {
ADD_DIAG("Mismatched data at position %d:", written);
ADD_DIAG("\tExpected: {%04x, %04x}", data[written].index, data[written].value);
ADD_DIAG("\tObserved: {%04x, %04x}", vdata->data[written].index, vdata->data[written].value);
return false;
}
written++;
}
if (data[written].index != 0 || data[written].value != 0) {
ADD_DIAG("data written %d", written);
ADD_DIAG("Insufficient data written, got only %d, additional expected:", written);
while (data[written].index != 0 && data[written].value != 0) {
ADD_DIAG("\t%04x %04x", data[written].index, data[written].value);
written++;
}
return false;
}
if (vdata->written > written) {
ADD_DIAG("More data written, expected only %d, got %d", written, vdata->written);
return false;
}
return true;
}
void x52_test_print_diagnostics(void)
{
int i;
for (i = 0; i < diag_count; i++) {
printf("# %s\n", diagnostic[i]);
}
}
static void _x52_test_print_data(struct ivpair *data)
{
while (data->index != 0 || data->value != 0) {
printf("%04x/%04x ", data->index, data->value);
data++;
}
puts("");
}
void x52_test_print_observed_data(libx52_device *dev)
{
struct x52_vendor_data *vdata = (struct x52_vendor_data *)dev->hdl;
printf("Observed: ");
_x52_test_print_data(vdata->data);
}
void x52_test_print_expected_data(struct ivpair *data)
{
printf("Expected: ");
_x52_test_print_data(data);
}
/*
* Initialize libx52, close any device handles, create a dummy handle
* and override the vendor command function.
*/
libx52_device *x52_test_init(void)
{
libx52_device *dev;
struct x52_vendor_data *vdata;
int rc;
rc = libx52_init(&dev);
if (rc != LIBX52_SUCCESS) {
fputs(libx52_strerror(rc), stderr);
exit(1);
}
(void)libx52_disconnect(dev);
/* Allocate memory for vendor data */
vdata = calloc(1, sizeof(*vdata));
if (vdata == NULL) {
perror("vendor data calloc");
libx52_exit(dev);
return NULL;
}
/* Reset the diagnostics buffers */
memset(diagnostic, 0, sizeof(diagnostic));
diag_count = 0;
/* We don't need the device handle in test code, repurpose it */
dev->hdl = (libusb_device_handle *)vdata;
/* Setup vendor command function */
dev->vendor_cmd_fn = x52_test_vendor_command;
return dev;
}
void x52_test_cleanup(libx52_device *dev)
{
free(dev->hdl);
dev->hdl = NULL;
libx52_exit(dev);
}

View File

@ -1,94 +0,0 @@
/*
* Saitek X52 Pro MFD & LED driver
* Common functionality for test programs
*
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#ifndef _TEST_COMMON_H
#define _TEST_COMMON_H
#include <stdint.h>
#include <stdbool.h>
#include "x52_common.h"
/* ivpair is a pair of index and value fields that are passed to the
* test vendor command function.
*/
struct ivpair {
uint16_t index;
uint16_t value;
};
#define MAX_SZ 100
struct x52_vendor_data {
int written;
struct ivpair data[MAX_SZ];
};
/*
* Initialize libx52, close any device handles, create a dummy handle
* and override the vendor command function.
*/
libx52_device *x52_test_init(void);
/*
* Check if expected data matches with written data. Terminate expected
* data with a pair of NULLs
*/
bool x52_test_assert_expected(libx52_device *dev, struct ivpair *data);
/* Print diagnostics to screen */
void x52_test_print_diagnostics(void);
/* Cleanup test data */
void x52_test_cleanup(libx52_device *dev);
void x52_test_print_observed_data(libx52_device *dev);
void x52_test_print_expected_data(struct ivpair *data);
#define PRINT_FAIL() printf("not ok %d %s\n", tc_id+1, test.test_case_id)
#define PRINT_PASS() printf("ok %d %s\n", tc_id+1, test.test_case_id)
#define TEST_STRUCT(...) struct test_case { \
const char *test_case_id; \
__VA_ARGS__ \
};
#define TEST_CASES const struct test_case test_cases[]
#define TEST_FUNC() void run_test(int tc_id)
#define TEST_INIT() \
struct libx52_device *dev = x52_test_init(); \
struct test_case test = test_cases[tc_id];
#define TEST_VERIFY(data) do { \
int test_rc; \
test_rc = libx52_update(dev); \
if (test_rc != LIBX52_SUCCESS) { \
PRINT_FAIL(); \
printf("# libx52_update failed, rc = %d\n", test_rc); \
return; \
} \
\
if (!x52_test_assert_expected(dev, data)) { \
PRINT_FAIL(); \
x52_test_print_diagnostics(); \
x52_test_print_expected_data(data); \
x52_test_print_observed_data(dev); \
return; \
} \
\
PRINT_PASS(); \
x52_test_cleanup(dev); \
} while(0)
#define TEST_MAIN() int main() { \
int i; \
size_t tc_count = sizeof(test_cases) / sizeof(test_cases[0]); \
printf("1..%lu\n", tc_count); \
for (i = 0; i < tc_count; i++) { \
run_test(i); \
} \
}
#endif // !defined _TEST_COMMON_H

View File

@ -1,122 +0,0 @@
/*
* Saitek X52 Pro MFD & LED driver
* Test program for validating LED sets
*
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "x52_common.h"
#include "test_common.h"
#include "x52_commands.h"
TEST_STRUCT (
libx52_led_id led_id;
libx52_led_state state;
int retval;
struct ivpair data[3];
)
#define X52_LED_CMD 0xb8
#define UNSUPPORTED(led, state) { #led "/" #state " unsupported", LIBX52_LED_ ## led, LIBX52_LED_STATE_ ## state, LIBX52_ERROR_NOT_SUPPORTED}
#define OFF_MONO(led) { #led "/Off", LIBX52_LED_## led, LIBX52_LED_STATE_OFF, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led) << 8)}, {0, 0}}}
#define ON(led) { #led "/On", LIBX52_LED_## led, LIBX52_LED_STATE_ON, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led) << 8) | 1}, {0, 0}}}
#define OFF_COLOR(led) { #led "/Off", LIBX52_LED_## led, LIBX52_LED_STATE_OFF, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led + 0) << 8)}, {X52_LED_CMD, ((LIBX52_LED_ ## led + 1) << 8)}, {0, 0}}}
#define RED(led) { #led "/Red", LIBX52_LED_## led, LIBX52_LED_STATE_RED, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led + 0) << 8) | 1}, {X52_LED_CMD, ((LIBX52_LED_ ## led + 1) << 8) | 0}, {0, 0}}}
#define AMBER(led) { #led "/Amber", LIBX52_LED_## led, LIBX52_LED_STATE_AMBER, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led + 0) << 8) | 1}, {X52_LED_CMD, ((LIBX52_LED_ ## led + 1) << 8) | 1}, {0, 0}}}
#define GREEN(led) { #led "/Green", LIBX52_LED_## led, LIBX52_LED_STATE_GREEN, LIBX52_SUCCESS, {{X52_LED_CMD, ((LIBX52_LED_ ## led + 0) << 8) | 0}, {X52_LED_CMD, ((LIBX52_LED_ ## led + 1) << 8) | 1}, {0, 0}}}
TEST_CASES = {
OFF_MONO(FIRE),
ON(FIRE),
UNSUPPORTED(FIRE, RED),
UNSUPPORTED(FIRE, AMBER),
UNSUPPORTED(FIRE, GREEN),
OFF_COLOR(A),
UNSUPPORTED(A, ON),
RED(A),
AMBER(A),
GREEN(A),
OFF_COLOR(B),
UNSUPPORTED(B, ON),
RED(B),
AMBER(B),
GREEN(B),
OFF_COLOR(D),
UNSUPPORTED(D, ON),
RED(D),
AMBER(D),
GREEN(D),
OFF_COLOR(E),
UNSUPPORTED(E, ON),
RED(E),
AMBER(E),
GREEN(E),
OFF_COLOR(T1),
UNSUPPORTED(T1, ON),
RED(T1),
AMBER(T1),
GREEN(T1),
OFF_COLOR(T2),
UNSUPPORTED(T2, ON),
RED(T2),
AMBER(T2),
GREEN(T2),
OFF_COLOR(T3),
UNSUPPORTED(T3, ON),
RED(T3),
AMBER(T3),
GREEN(T3),
OFF_COLOR(POV),
UNSUPPORTED(POV, ON),
RED(POV),
AMBER(POV),
GREEN(POV),
OFF_COLOR(CLUTCH),
UNSUPPORTED(CLUTCH, ON),
RED(CLUTCH),
AMBER(CLUTCH),
GREEN(CLUTCH),
OFF_MONO(THROTTLE),
ON(THROTTLE),
UNSUPPORTED(THROTTLE, RED),
UNSUPPORTED(THROTTLE, AMBER),
UNSUPPORTED(THROTTLE, GREEN),
};
TEST_FUNC()
{
TEST_INIT();
/* Set the X52Pro flag in dev->flags, otherwise libx52_set_led_state will
* always return not supported
*/
dev->flags = 1;
int rc = libx52_set_led_state(dev, test.led_id, test.state);
if (rc != test.retval) {
PRINT_FAIL();
printf("# Expected retval %d, got %d\n", test.retval, rc);
return;
}
TEST_VERIFY(test.data);
}
TEST_MAIN()

View File

@ -1,60 +0,0 @@
/*
* Saitek X52 Pro MFD & LED driver
* Test program for validating offset calculation
*
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "x52_common.h"
#include "test_common.h"
TEST_STRUCT (
int offset_primary;
int offset_secondary;
libx52_clock_id clock_id;
uint16_t x52_clock;
uint16_t x52_offset;
)
#define TEST(id, o1, o23, offs) {id, o1, o23, LIBX52_CLOCK_2, 0xc1, offs}, {id, o1, o23, LIBX52_CLOCK_3, 0xc2, offs}
TEST_CASES = {
TEST("Etc/UTC+24|Etc/UTC-24", -1440, +1440, 0),
TEST("Etc/UTC-24|Etc/UTC+24", +1440, -1440, 0x0400), // Negative 0
TEST("Honolulu|Auckland", -600, +720, 0x0478), // -2 hours
TEST("Auckland|Honolulu", +720, -600, 0x0078), // +2 hours
TEST("Etc/UTC+12|Etc/UTC-14", -720, +840, 0x078), // +2 hours
TEST("Etc/UTC-14|Etc/UTC+12", +840, -720, 0x478), // -2 hours
TEST("PDT|UTC", -420, 0, 0x1a4), // +7 hours
TEST("UTC|PDT", 0, -420, 0x5a4), // -7 hours
TEST("PST|UTC", -480, 0, 0x1e0), // +8 hours
TEST("UTC|PST", 0, -480, 0x5e0), // -8 hours
TEST("Etc/UTC+12|Etc/UTC-12", -720, +720, 0),
TEST("Etc/UTC-12|Etc/UTC+12", +720, -720, 0x0400),
};
TEST_FUNC()
{
TEST_INIT();
int rc;
struct ivpair data[2] = { 0 };
dev->timezone[LIBX52_CLOCK_1] = test.offset_primary;
rc = libx52_set_clock_timezone(dev, test.clock_id, test.offset_secondary);
if (rc != LIBX52_SUCCESS) {
PRINT_FAIL();
printf("# set_clock_timezone failed, rc = %d\n", rc);
return;
}
data[0].index = test.x52_clock;
data[0].value = test.x52_offset;
TEST_VERIFY(data);
}
TEST_MAIN();

View File

@ -25,8 +25,6 @@
#define X52_MFD_LINES 3
#define X52_MFD_CLOCKS 3
typedef int (*x52_vendor_command)(libx52_device *x52, uint16_t index, uint16_t value);
struct x52_mfd_line {
uint8_t text[X52_MFD_LINE_SIZE];
uint8_t length;
@ -53,7 +51,6 @@ struct libx52_device {
int timezone[X52_MFD_CLOCKS];
libx52_clock_format time_format[X52_MFD_CLOCKS];
x52_vendor_command vendor_cmd_fn;
};
/** Flag bits */
@ -108,6 +105,5 @@ static inline uint32_t tst_bit(uint32_t *value, uint32_t bit)
}
int _x52_translate_libusb_error(enum libusb_error errcode);
int _x52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value);
#endif /* !defined X52JOY_COMMON_H */

View File

@ -66,11 +66,18 @@ int _x52_translate_libusb_error(enum libusb_error errcode)
};
}
int _x52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
int libx52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
{
int j;
int rc = 0;
/* It is possible for the vendor command to be called when the joystick
* is not connected. Check for this and return an appropriate error.
*/
if (!x52->hdl) {
return LIBX52_ERROR_NO_DEVICE;
}
/* Allow retry in case of failure */
for (j = 0; j < 3; j++) {
rc = libusb_control_transfer(x52->hdl,
@ -82,22 +89,6 @@ int _x52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
}
}
return rc;
}
int libx52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
{
int rc = 0;
/* It is possible for the vendor command to be called when the joystick
* is not connected. Check for this and return an appropriate error.
*/
if (!x52->hdl || !x52->vendor_cmd_fn) {
return LIBX52_ERROR_NO_DEVICE;
}
rc = (*(x52->vendor_cmd_fn))(x52, index, value);
/* Handle device removal */
if (rc == LIBUSB_ERROR_NO_DEVICE) {
/* Physical device has likely been disconnected, disconnect the virtual

View File

@ -61,7 +61,6 @@ int libx52_disconnect(libx52_device *dev)
libusb_close(dev->hdl);
dev->hdl = NULL;
dev->flags = 0;
dev->vendor_cmd_fn = NULL;
}
return LIBX52_SUCCESS;
@ -103,9 +102,6 @@ int libx52_connect(libx52_device *dev)
dev->hdl = hdl;
/* Use default vendor command function */
dev->vendor_cmd_fn = _x52_vendor_command;
if (libx52_device_is_x52pro(desc.idProduct)) {
set_bit(&(dev->flags), X52_FLAG_IS_PRO);
}