mirror of https://github.com/nirenjan/libx52.git
Add generated tests to verify libx52 functionality
This change adds a suite of tests in JSON format using a Python script to generate the cmocka based test program. Because we need to wrap some of libusb functionality, we need to rebuild and relink the libx52 sources with the -Wl,--wrap option.pull/22/head
parent
bf9b1bdfbd
commit
9f37cde784
|
@ -33,11 +33,26 @@ pkgconfig_DATA = libx52.pc
|
|||
|
||||
if HAVE_CMOCKA
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
|
||||
TESTS = libx52test
|
||||
check_PROGRAMS = libx52test
|
||||
|
||||
libx52test_SOURCES = test_libx52.c $(libx52_la_SOURCES)
|
||||
libx52test_CFLAGS = @LIBUSB_CFLAGS@ -DLOCALEDIR='"$(localedir)"' -I $(top_srcdir)
|
||||
libx52test_CFLAGS += -DGENERATED_TESTS='"test_libx52.c"'
|
||||
libx52test_LDFLAGS = -Wl,--wrap=libusb_control_transfer @CMOCKA_LIBS@ @LIBUSB_LIBS@
|
||||
libx52test_LDADD = libx52.la
|
||||
|
||||
CLEANFILES = test_libx52.c
|
||||
test_libx52.c: $(srcdir)/x52_test_gen.py $(srcdir)/x52_tests.json
|
||||
$(AM_V_GEN) $(PYTHON) $(srcdir)/x52_test_gen.py $(srcdir)/x52_tests.json > $@
|
||||
endif
|
||||
|
||||
# Extra files that need to be in the distribution
|
||||
EXTRA_DIST = libx52.h x52_commands.h x52_common.h README.md
|
||||
|
||||
# Add test files to the distribution
|
||||
EXTRA_DIST += x52_test_gen.py x52_tests.json
|
||||
|
||||
# Add documentation files to the distribution
|
||||
EXTRA_DIST += \
|
||||
doc/main.dox \
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
#!/usr/bin/env python3
|
||||
"""libx52 test generator program, writes test program to stdout"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
_TEST_FILE_HEADER = """/*
|
||||
* libx52 test program
|
||||
*
|
||||
* This file is automatically generated. DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "x52_common.h"
|
||||
|
||||
static int group_setup(void **state)
|
||||
{
|
||||
libx52_device *dev;
|
||||
int rc;
|
||||
|
||||
rc = libx52_init(&dev);
|
||||
if (rc != LIBX52_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Disconnect any potentially connected joysticks */
|
||||
(void)libx52_disconnect(dev);
|
||||
|
||||
/* Create a dummy handle so that libx52_update doesn't abort early */
|
||||
dev->hdl = (void *)(uintptr_t)(-1);
|
||||
|
||||
*state = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_teardown(void **state)
|
||||
{
|
||||
libx52_device *dev = *state;
|
||||
|
||||
dev->hdl = NULL;
|
||||
libx52_exit(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_setup(void **state)
|
||||
{
|
||||
libx52_device *dev = *state;
|
||||
void *context = dev->ctx;
|
||||
void *handle = dev->hdl;
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
dev->ctx = context;
|
||||
dev->hdl = handle;
|
||||
/* Set flags to 1 to indicate that we are testing X52 Pro */
|
||||
dev->flags = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __wrap_libusb_control_transfer(libusb_device_handle *dev_handle,
|
||||
uint8_t request_type,
|
||||
uint8_t bRequest,
|
||||
uint16_t wValue,
|
||||
uint16_t wIndex,
|
||||
unsigned char *data,
|
||||
uint16_t wLength,
|
||||
unsigned int timeout)
|
||||
{
|
||||
function_called();
|
||||
check_expected(wIndex);
|
||||
check_expected(wValue);
|
||||
assert_int_equal(request_type,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT);
|
||||
assert_int_equal(bRequest, 0x91);
|
||||
assert_null(data);
|
||||
assert_int_equal(wLength, 0);
|
||||
assert_int_equal(timeout, 5000);
|
||||
|
||||
return mock();
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
_TEST_FUNCTION_HEADER = """
|
||||
static void {}(void **state)
|
||||
{{
|
||||
libx52_device *dev = *state;
|
||||
int rc;
|
||||
"""
|
||||
|
||||
_TEST_FUNCTION_FOOTER_NORMAL = """
|
||||
assert_int_equal(rc, LIBX52_SUCCESS);
|
||||
|
||||
rc = libx52_update(dev);
|
||||
assert_int_equal(rc, LIBX52_SUCCESS);
|
||||
}
|
||||
"""
|
||||
|
||||
_TEST_FUNCTION_FOOTER_ERROR = """
|
||||
assert_int_equal(rc, LIBX52_ERROR_{});
|
||||
}}
|
||||
"""
|
||||
|
||||
class Test():
|
||||
"""Test case class, single test"""
|
||||
|
||||
def __init__(self, group, obj):
|
||||
"""Load test case from an object"""
|
||||
self.function = group.function
|
||||
self.name = group.name
|
||||
self.params_prefix = group.params_prefix
|
||||
self.params = obj["params"]
|
||||
if len(self.params_prefix) < len(self.params):
|
||||
self.params_prefix.extend([''] * (len(self.params) - len(self.params_prefix)))
|
||||
|
||||
self.output = obj.get("output", [])
|
||||
self.retval = obj.get("retval", "")
|
||||
|
||||
def definition(self):
|
||||
test_name = self.name + '_' + '_'.join(p.strip('"') for p in self.params)
|
||||
return test_name.lower()
|
||||
|
||||
def print(self):
|
||||
print(_TEST_FUNCTION_HEADER.format(self.definition()))
|
||||
|
||||
if self.output:
|
||||
print(" expect_function_calls(__wrap_libusb_control_transfer, {});".format(len(self.output)))
|
||||
print(" will_return_count(__wrap_libusb_control_transfer, LIBUSB_SUCCESS, {});".format(len(self.output)))
|
||||
|
||||
for idx, val in self.output:
|
||||
print(" expect_value(__wrap_libusb_control_transfer, wIndex, 0x{});".format(idx))
|
||||
print(" expect_value(__wrap_libusb_control_transfer, wValue, 0x{});".format(val))
|
||||
|
||||
params = ', '.join(''.join(p) for p in zip(self.params_prefix, self.params))
|
||||
print(" rc = {}(dev, {});".format(self.function, params))
|
||||
|
||||
if self.retval:
|
||||
print(_TEST_FUNCTION_FOOTER_ERROR.format(self.retval))
|
||||
else:
|
||||
print(_TEST_FUNCTION_FOOTER_NORMAL);
|
||||
|
||||
_TEST_GROUP_HEADER = "const struct CMUnitTest tests[] = {"
|
||||
_TEST_GROUP_FOOTER = """};
|
||||
|
||||
"""
|
||||
|
||||
class TestGroup():
|
||||
"""Test group class, contains multiple tests"""
|
||||
|
||||
def __init__(self, name, obj):
|
||||
"""Load test cases from an object"""
|
||||
self.name = name
|
||||
self.function = obj["function"]
|
||||
self.setup_hook = obj.get("setup_hook")
|
||||
self.params_prefix = obj.get("params_prefix", [])
|
||||
self.tests = []
|
||||
for test in obj["tests"]:
|
||||
self.tests.append(Test(self, test))
|
||||
|
||||
def definition(self):
|
||||
return self.name.lower() + "_tests"
|
||||
|
||||
def print(self):
|
||||
"""Print the test group"""
|
||||
# Print the test definitions first
|
||||
for test in self.tests:
|
||||
test.print()
|
||||
|
||||
# Print the list of test cases
|
||||
# print(_TEST_GROUP_HEADER.format(self.definition()))
|
||||
# for test in self.tests:
|
||||
# print(" cmocka_unit_test_setup({}, test_setup),".format(test.definition()))
|
||||
# print(_TEST_GROUP_FOOTER)
|
||||
|
||||
_MAIN_HEADER = """
|
||||
int main(void)
|
||||
{
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
|
||||
"""
|
||||
_MAIN_FOOTER = """
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
_MAIN = """
|
||||
int main(void)
|
||||
{
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
cmocka_run_group_tests(tests, group_setup, group_teardown);
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
class TestSuite():
|
||||
"""Test suite class, contains multiple test cases"""
|
||||
|
||||
def __init__(self, file):
|
||||
"""Load test suite"""
|
||||
with open(file, 'r') as infile:
|
||||
self.data = json.load(infile)
|
||||
self.groups = []
|
||||
for group, obj in self.data.items():
|
||||
self.groups.append(TestGroup(group, obj))
|
||||
|
||||
def print(self):
|
||||
print(_TEST_FILE_HEADER)
|
||||
|
||||
for group in self.groups:
|
||||
group.print()
|
||||
|
||||
print(_TEST_GROUP_HEADER)
|
||||
for group in self.groups:
|
||||
for test in group.tests:
|
||||
print(" cmocka_unit_test_setup({}, test_setup),".format(test.definition()))
|
||||
print(_TEST_GROUP_FOOTER)
|
||||
|
||||
print(_MAIN)
|
||||
# print(_MAIN_HEADER)
|
||||
# for group in self.groups:
|
||||
# print(' cmocka_run_group_tests_name("{}", {}, group_setup, group_teardown);'.format(
|
||||
# group.name, group.definition()))
|
||||
# print(_MAIN_FOOTER)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
sys.stderr.write('Usage: %s <test-definitions>\n' %
|
||||
sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
TestSuite(sys.argv[1]).print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue