diff options
author | taeyoung <ty317.kim@samsung.com> | 2016-09-23 19:46:01 +0900 |
---|---|---|
committer | taeyoung <ty317.kim@samsung.com> | 2016-09-23 19:46:39 +0900 |
commit | af80c789ed18bffe59ff815f0fddabbda56b9442 (patch) | |
tree | 2edbc50a8a91662cd022f9b5c5d0693f6a8b4071 /tests | |
parent | 6521a52d94e2a43f69ca856bf921b0362c910e21 (diff) | |
download | libusbg-af80c789ed18bffe59ff815f0fddabbda56b9442.tar.gz libusbg-af80c789ed18bffe59ff815f0fddabbda56b9442.tar.bz2 libusbg-af80c789ed18bffe59ff815f0fddabbda56b9442.zip |
Imported Upstream version 0.0.1
Change-Id: I7f9821384b1c58f400c5e4935edb8b89f30d503e
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 14 | ||||
-rw-r--r-- | tests/test.c | 2697 | ||||
-rwxr-xr-x | tests/test.sh | 78 | ||||
-rw-r--r-- | tests/usbg-io-wrappers.c | 203 | ||||
-rw-r--r-- | tests/usbg-test.c | 1389 | ||||
-rw-r--r-- | tests/usbg-test.h | 549 |
6 files changed, 4930 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..01feea4 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,14 @@ +check_PROGRAMS = test +test_SOURCES = test.c usbg-test.c usbg-io-wrappers.c +test_LDFLAGS = -ldl +test_LDFLAGS += $(CMOCKA_LIBS) +test_LDFLAGS += $(LIBCONFIG_LIBS) +test_LDADD = ./libusbgx.so +test_CPPFLAGS = -I$(top_srcdir)/include/ + +./libusbgx.so: + -ln -s $(top_srcdir)/src/.libs/libusbgx.so* . +CLEANFILES = libusbgx.so* + +check_SCRIPTS = ./test.sh +TESTS = $(check_SCRIPTS) diff --git a/tests/test.c b/tests/test.c new file mode 100644 index 0000000..bd17af9 --- /dev/null +++ b/tests/test.c @@ -0,0 +1,2697 @@ +#include <usbg/usbg.h> +#include <stdio.h> +#include <stdarg.h> +#include <setjmp.h> +#include <cmocka.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <getopt.h> +#include <time.h> + +#ifdef HAS_LIBCONFIG +#include <libconfig.h> +#endif + +#include "usbg-test.h" + +/** + * @file tests/test.c + */ + +#define USBG_TEST(name, test, setup, teardown) \ + {name, test, setup, teardown} + +#define FILLED_STR(len, c) \ + { [0 ... len - 2] = c, [len - 1] = '\0' } + +/* two levels of macros allow to strigify result of macro expansion */ +#define STR(s) #s +#define XSTR(s) STR(s) +/* unique string */ +#define UNIQUE XSTR(__COUNTER__) + +#define FUNC_FROM_TYPE(t) { \ + .type = t, \ + .instance = "instance"UNIQUE \ +} + +#define CONF_FROM_BOUND(b) { \ + .label = "c", \ + .id = __COUNTER__, \ + .bound_funcs = b \ +} + +static usbg_gadget_attrs min_gadget_attrs = { + .bcdUSB = 0x0000, + .bDeviceClass = 0x0, + .bDeviceSubClass = 0x0, + .bDeviceProtocol = 0x0, + .bMaxPacketSize0 = 0x0, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = 0x0000 +}; + +static usbg_gadget_attrs max_gadget_attrs = { + .bcdUSB = 0xffff, + .bDeviceClass = 0xff, + .bDeviceSubClass = 0xff, + .bDeviceProtocol = 0xff, + .bMaxPacketSize0 = 0xff, + .idVendor = 0xffff, + .idProduct = 0xffff, + .bcdDevice = 0xffff +}; + +/* PATH_MAX is limit for path length */ +#define LONG_PATH_LEN PATH_MAX/2 +static char long_path_str[] = FILLED_STR(LONG_PATH_LEN, 'x'); + +/* NAME_MAX is limit for filename length */ +static char long_usbg_string[] = FILLED_STR(NAME_MAX, 'x'); + +static usbg_config_strs simple_config_strs= { + .configuration = "configuration string" +}; + +static usbg_config_attrs max_config_attrs = { + .bmAttributes = 0xff, + .bMaxPower = 0xff +}; + +static usbg_config_attrs min_config_attrs = { + .bmAttributes = 0x00, + .bMaxPower = 0x00 +}; + +/** + * @brief Simplest udcs names + * @details Used to go through init when testing other things + */ +static char *simple_udcs[] = { + "UDC1", + "UDC2", + NULL +}; + +static char *long_udcs[] = { + long_usbg_string, + "UDC1", + NULL +}; + +/** + * @brief Simplest functions names + * @details Used to go through init when testing other things + */ +static struct test_function simple_funcs[] = { + FUNC_FROM_TYPE(F_ECM), + FUNC_FROM_TYPE(F_ACM), + TEST_FUNCTION_LIST_END +}; + +/** + * @brief All functions types + * @details When testing with this in state, check if all func types are + * processed correctly + */ +static struct test_function all_funcs[] = { + FUNC_FROM_TYPE(F_SERIAL), + FUNC_FROM_TYPE(F_ACM), + FUNC_FROM_TYPE(F_OBEX), + FUNC_FROM_TYPE(F_ECM), + FUNC_FROM_TYPE(F_SUBSET), + FUNC_FROM_TYPE(F_NCM), + FUNC_FROM_TYPE(F_EEM), + FUNC_FROM_TYPE(F_RNDIS), + FUNC_FROM_TYPE(F_PHONET), + FUNC_FROM_TYPE(F_FFS), + TEST_FUNCTION_LIST_END +}; + +static struct test_function same_type_funcs[] = { + FUNC_FROM_TYPE(F_SERIAL), + FUNC_FROM_TYPE(F_SERIAL), + FUNC_FROM_TYPE(F_SERIAL), + TEST_FUNCTION_LIST_END +}; + +/** + * @brief No functions at all + * @details Check if gadget with no functions (or config with no bindings) + * is processed correctly. + */ +static struct test_function no_funcs[] = { + TEST_FUNCTION_LIST_END +}; + +/** + * @brief Simple configs + * @details Used to pass through init when testing other things + */ +static struct test_config simple_confs[] = { + CONF_FROM_BOUND(simple_funcs), + TEST_CONFIG_LIST_END +}; + +/** + * @brief Configs bound to all avaible function types + */ +static struct test_config all_bindings_confs[] = { + CONF_FROM_BOUND(no_funcs), + CONF_FROM_BOUND(all_funcs), + TEST_CONFIG_LIST_END +}; + +#define GADGET(n, u, c, f) \ + { \ + .name = n, \ + .udc = u, \ + .configs = c, \ + .functions = f \ + } + +/** + * @brief Simplest gadget + */ +static struct test_gadget simple_gadgets[] = { + GADGET("g1", "UDC1", simple_confs, simple_funcs), + TEST_GADGET_LIST_END +}; + +/** + * @brief Gadgets with all avaible functions + */ +static struct test_gadget all_funcs_gadgets[] = { + GADGET("all_funcs_gadget1", "UDC1", all_bindings_confs, all_funcs), + TEST_GADGET_LIST_END +}; + +static struct test_gadget long_udc_gadgets[] = { + GADGET("long_udc_gadgets", long_usbg_string, simple_confs, simple_funcs), + TEST_GADGET_LIST_END +}; + +struct test_function_attrs_data { + struct test_state *state; + usbg_function_attrs *attrs; +}; +struct test_data { + struct test_state *state; + struct usbg_state *usbg_state; +}; + +#define FUNC_ATTRS(t, label, a...) { \ + .header = { \ + .attrs_type = t \ + }, \ + .attrs = { \ + .label = { a } \ + }, \ +} + +static usbg_function_attrs simple_serial_attrs = FUNC_ATTRS(USBG_F_ATTRS_SERIAL, serial, 42); +static usbg_function_attrs simple_net_attrs = FUNC_ATTRS(USBG_F_ATTRS_NET, net, {}, {}, "if", 1); +static usbg_function_attrs simple_phonet_attrs = FUNC_ATTRS(USBG_F_ATTRS_PHONET, phonet, "if"); +static usbg_function_attrs writable_serial_attrs = FUNC_ATTRS(USBG_F_ATTRS_SERIAL, serial, 0); +static usbg_function_attrs writable_net_attrs = FUNC_ATTRS(USBG_F_ATTRS_NET, net, {}, {}, "", 42); +static usbg_function_attrs writable_phonet_attrs = FUNC_ATTRS(USBG_F_ATTRS_PHONET, phonet, ""); +static usbg_function_attrs simple_ffs_attrs = FUNC_ATTRS(USBG_F_ATTRS_FFS, ffs, "0"); +static usbg_function_attrs writable_ffs_attrs = FUNC_ATTRS(USBG_F_ATTRS_FFS, ffs, ""); + +struct test_gadget_strs_data { + struct test_state *state; + usbg_gadget_strs *strs; +}; + +#define STATE(p, g, u) { \ + .configfs_path = p, \ + .gadgets = g, \ + .udcs = u \ +} + +/** + * @brief Simple state + */ +static struct test_state simple_state = STATE("config", simple_gadgets, simple_udcs); + +/** + * @brief State with all functions avaible + */ +static struct test_state all_funcs_state = STATE("all_funcs_configfs", all_funcs_gadgets, simple_udcs); + +static struct test_state long_path_state = STATE(long_path_str, simple_gadgets, simple_udcs); + +static struct test_state long_udc_state = STATE("simple_path", long_udc_gadgets, long_udcs); + +static usbg_config_attrs *get_random_config_attrs() +{ + usbg_config_attrs *ret; + + ret = safe_malloc(sizeof(*ret)); + + srand(time(NULL)); + ret->bmAttributes = rand() % max_config_attrs.bmAttributes; + ret->bMaxPower = rand() % max_config_attrs.bMaxPower; + + return ret; +} + +static usbg_gadget_attrs *get_random_gadget_attrs() +{ + usbg_gadget_attrs *ret; + + ret = safe_malloc(sizeof(*ret)); + + srand(time(NULL)); + ret->bcdUSB = rand() % max_gadget_attrs.bcdUSB; + ret->bDeviceClass = rand() % max_gadget_attrs.bDeviceClass; + ret->bDeviceSubClass = rand() % max_gadget_attrs.bDeviceSubClass; + ret->bDeviceProtocol = rand() % max_gadget_attrs.bDeviceProtocol; + ret->bMaxPacketSize0 = rand() % max_gadget_attrs.bMaxPacketSize0; + ret->idVendor = rand() % max_gadget_attrs.idVendor; + ret->idProduct = rand() % max_gadget_attrs.idProduct; + ret->bcdDevice = rand() % max_gadget_attrs.bcdDevice; + + return ret; +} + +/** + * @brief Add given attributes to all configs in state + * @return Prepared state where configs has given attributes + */ +static void *prepare_state_with_config_attrs(struct test_state *state, + usbg_config_attrs *attrs) +{ + struct test_gadget *tg; + struct test_config *tc; + + for (tg = state->gadgets; tg->name; ++tg) + for (tc = tg->configs; tc->label; ++tc) + tc->attrs = attrs; + + state = prepare_state(state); + return state; +} + +static int setup_max_config_attrs_state(void **state) +{ + *state = prepare_state_with_config_attrs(&simple_state, &max_config_attrs); + return 0; +} + +static int setup_min_config_attrs_state(void **state) +{ + *state = prepare_state_with_config_attrs(&simple_state, &min_config_attrs); + return 0; +} + +static int setup_random_config_attrs_state(void **state) +{ + *state = prepare_state_with_config_attrs(&simple_state, get_random_config_attrs()); + return 0; +} + +static int setup_simple_config_strs_state(void **state) +{ + struct test_gadget *tg; + struct test_config *tc; + + for (tg = simple_state.gadgets; tg->name; ++tg) + for (tc = tg->configs; tc->label; ++tc) + tc->strs = &simple_config_strs; + + *state = prepare_state(&simple_state); + return 0; +} + +/** + * @brief Prepare test_state with one gadget containing given function list + * @details For testing only functions. We put them in a gadget as simply + * as possible. + * @param[in] func Pointer to list of functions + * @return Pointer to test state with given functions + */ +static struct test_state *put_func_in_state(struct test_function *func) +{ + struct test_state *st; + struct test_gadget *g; + struct test_config *c; + char **udcs; + + st = safe_calloc(1, sizeof(*st)); + /* Do not need config */ + c = safe_calloc(1, sizeof(*c)); + g = safe_calloc(2, sizeof(*g)); + udcs = safe_calloc(2, sizeof(*udcs)); + + g[0].name = "g1"; + g[0].udc = "UDC1"; + g[0].configs = c; + g[0].functions = func; + g[0].writable = 1; + + udcs[0] = "UDC1"; + g[0].writable = 1; + + st->configfs_path = "config"; + st->gadgets = g; + st->udcs = udcs; + st->writable = 1; + + st = prepare_state(st); + + return st; +} + +/** + * @brief Setup simple state with some gadgets, configs and functions + */ +static int setup_simple_state(void **state) +{ + *state = prepare_state(&simple_state); + return 0; +} + +/** + * @brief Setup state with all avaible functions + */ +static int setup_all_funcs_state(void **state) +{ + *state = prepare_state(&all_funcs_state); + return 0; +} + +/** + * @brief Setup state with few functions of the same type + */ +static int setup_same_type_funcs_state(void **state) +{ + *state = put_func_in_state(same_type_funcs); + return 0; +} + +/** + * @brief Setup state with very long path name + */ +static int setup_long_path_state(void **state) +{ + *state = prepare_state(&long_path_state); + return 0; +} + +/** + * @brief Setup state with long udc name + */ +static int setup_long_udc_state(void **state) +{ + *state = prepare_state(&long_udc_state); + return 0; +} + +/** + * @brief Setup state with gadget strings of random length + * @param[out] state Pointer to pointer to test_gadget_strs_data structure + * with initialized state and strings + */ +static int setup_random_len_gadget_strs_data(void **state) +{ + usbg_gadget_strs *strs; + struct test_gadget_strs_data *data; + + /* will fill memory with zeros */ + strs = safe_calloc(1, sizeof(*strs)); + data = safe_malloc(sizeof(*data)); + + srand(time(NULL)); + + memset(strs->str_ser, 'x', rand() % USBG_MAX_STR_LENGTH); + memset(strs->str_mnf, 'x', rand() % USBG_MAX_STR_LENGTH); + memset(strs->str_prd, 'x', rand() % USBG_MAX_STR_LENGTH); + + data->strs = strs; + + data->state = prepare_state(&simple_state); + *state = data; + return 0; +} + +static void *setup_f_attrs(int f_type, usbg_function_attrs *attrs) +{ + struct test_function_attrs_data *data; + struct test_function *func; + + data = safe_malloc(sizeof(*data)); + + func = safe_calloc(2, sizeof(*func)); + func[0].type = f_type; + func[0].instance = "0"; + func[0].writable = 1; + + data->state = put_func_in_state(func); + data->attrs = attrs; + return data; +} + +static int setup_f_serial_attrs(void **state) +{ + *state = setup_f_attrs(F_SERIAL, &simple_serial_attrs); + return 0; +} + +static int setup_f_serial_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_SERIAL, &writable_serial_attrs); + return 0; +} + +static int setup_f_acm_attrs(void **state) +{ + *state = setup_f_attrs(F_ACM, &simple_serial_attrs); + return 0; +} + +static int setup_f_acm_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_ACM, &writable_serial_attrs); + return 0; +} + +static int setup_f_obex_attrs(void **state) +{ + *state = setup_f_attrs(F_OBEX, &simple_serial_attrs); + return 0; +} + +static int setup_f_obex_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_OBEX, &writable_serial_attrs); + return 0; +} + +static int setup_f_ecm_attrs(void **state) +{ + *state = setup_f_attrs(F_ECM, &simple_net_attrs); + return 0; +} + +static int setup_f_ecm_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_ECM, &writable_net_attrs); + return 0; +} + +static int setup_f_subset_attrs(void **state) +{ + *state = setup_f_attrs(F_SUBSET, &simple_net_attrs); + return 0; +} + +static int setup_f_subset_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_SUBSET, &writable_net_attrs); + return 0; +} + +static int setup_f_ncm_attrs(void **state) +{ + *state = setup_f_attrs(F_NCM, &simple_net_attrs); + return 0; +} + +static int setup_f_ncm_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_NCM, &writable_net_attrs); + return 0; +} + +static int setup_f_eem_attrs(void **state) +{ + *state = setup_f_attrs(F_EEM, &simple_net_attrs); + return 0; +} + +static int setup_f_eem_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_EEM, &writable_net_attrs); + return 0; +} + +static int setup_f_rndis_attrs(void **state) +{ + *state = setup_f_attrs(F_RNDIS, &simple_net_attrs); + return 0; +} + +static int setup_f_rndis_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_RNDIS, &writable_net_attrs); + return 0; +} + +static int setup_f_phonet_attrs(void **state) +{ + *state = setup_f_attrs(F_PHONET, &simple_phonet_attrs); + return 0; +} + +static int setup_f_phonet_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_PHONET, &writable_phonet_attrs); + return 0; +} + +static int setup_f_ffs_attrs(void **state) +{ + *state = setup_f_attrs(F_FFS, &simple_ffs_attrs); + return 0; +} + +static int setup_f_ffs_writable_attrs(void **state) +{ + *state = setup_f_attrs(F_FFS, &writable_ffs_attrs); + return 0; +} + +/** + * @brief Tests usbg_get_gadget function with given state + * @details Check if gadgets are returned correctly + */ +static void test_get_gadget(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_gadget(ts, s, assert_gadget_equal); +} + +/** + * @brief Tests usbg_get_gadget with non-existing gadget name + * @details Check if get_gadget will not find non-existing gadgets and + * will not cause crash. + */ +static void test_get_gadget_fail(void **state) +{ + usbg_gadget *g = NULL; + usbg_state *s = NULL; + struct test_state *st; + + safe_init_with_state(state, &st, &s); + + g = usbg_get_gadget(s, "non-existing-gadget"); + assert_null(g); +} + +/** + * @brief Tests usbg_get_first_gadget function + * @details Check if gadget returned by get_first_gadget is actually first one + */ +static void test_get_first_gadget(void **state) +{ + usbg_gadget *g = NULL; + usbg_state *s = NULL; + struct test_state *st; + + safe_init_with_state(state, &st, &s); + + g = usbg_get_first_gadget(s); + assert_non_null(g); + assert_gadget_equal(g, &st->gadgets[0]); +} + +/** + * @brief Tests get_first_gadget with invalid arguments + */ +static void test_get_first_gadget_fail(void **state) +{ + usbg_gadget *g; + + g = usbg_get_first_gadget(NULL); + assert_null(g); +} + +static void try_get_gadget_name(usbg_gadget *g, struct test_gadget *tg) +{ + const char *name; + + name = usbg_get_gadget_name(g); + assert_string_equal(name, tg->name); +} + +/** + * @brief Tests getting name of gadget + * @details Check if gadget name is returned correctly + */ +static void test_get_gadget_name(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_gadget(ts, s, try_get_gadget_name); +} + +static void try_get_gadget_name_len(usbg_gadget *g, struct test_gadget *tg) +{ + int len; + int expected; + + expected = strlen(tg->name); + len = usbg_get_gadget_name_len(g); + assert_int_equal(len, expected); +} + +/** + * @brief Tests getting name length of gadget + * @details Check if returned name length is equal original + */ +static void test_get_gadget_name_len(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_gadget(ts, s, try_get_gadget_name_len); +} + +/** + * @brief Tests getting name of gadget with invalid arguments + * @details Check if trying to get name of wrong (non-existing) gadget + * will not cause crash, but return NULL as expected. + */ +static void test_get_gadget_name_fail(void **state) +{ + const char *name; + + name = usbg_get_gadget_name(NULL); + assert_null(name); +} + +static void try_cpy_gadget_name(usbg_gadget *g, struct test_gadget *tg) +{ + char name[USBG_MAX_NAME_LENGTH]; + int ret; + + ret = usbg_cpy_gadget_name(g, name, USBG_MAX_NAME_LENGTH); + assert_int_equal(ret, USBG_SUCCESS); + assert_string_equal(name, tg->name); +} + +/** + * @brief Tests copying gadget's name + * @details Check if copying gadget name copy actual name correctly + */ +static void test_cpy_gadget_name(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_gadget(ts, s, try_cpy_gadget_name); +} + +/** + * @brief Test copying gadet name with invalid arguments + * @details Check if trying to copy gadget name into non-existing buffer, + * or give invalid buffer length, or invalid gadget will be handled by library + * and return correct error codes + */ +static void test_cpy_gadget_name_fail(void **state) +{ + usbg_gadget *g = NULL; + usbg_state *s = NULL; + struct test_state *st; + int i = 0; + char name[USBG_MAX_NAME_LENGTH]; + int ret; + + safe_init_with_state(state, &st, &s); + + for (i = 0; st->gadgets[i].name; i++) { + g = usbg_get_gadget(s, st->gadgets[i].name); + assert_non_null(g); + + ret = usbg_cpy_gadget_name(g, name, 0); + assert_int_equal(ret, USBG_ERROR_INVALID_PARAM); + + ret = usbg_cpy_gadget_name(g, NULL, USBG_MAX_NAME_LENGTH); + assert_int_equal(ret, USBG_ERROR_INVALID_PARAM); + } + + ret = usbg_cpy_gadget_name(NULL, name, USBG_MAX_NAME_LENGTH); + assert_int_equal(ret, USBG_ERROR_INVALID_PARAM); +} + +/** + * @brief Tests init by comparing test state and usbg state + * @details Check if usbg state after init match given state and + * if init returned success code + */ +static void test_init(void **state) +{ + usbg_state *s = NULL; + struct test_state *st; + + safe_init_with_state(state, &st, &s); + + assert_state_equal(s, st); +} + +/** + * @brief Test getting function by name + * @param[in] state Pointer to pointer to correctly initialized test_state structure + */ +static void test_get_function(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_function(ts, s, assert_func_equal); +} + +/** + * @brief Tests usbg_get_function with some non-existing functions + * @details Check if get function will return NULL, when invalid + * functions names and types are passed as arguments and will not cause crash. + * @param[in] state Pointer to pointer to correctly initialized test_state structure + */ +static void test_get_function_fail(void **state) +{ + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_function *f = NULL; + struct test_state *st; + + safe_init_with_state(state, &st, &s); + + g = usbg_get_first_gadget(s); + assert_non_null(g); + + f = usbg_get_function(g, F_ACM, "non-existing-instance"); + assert_null(f); + + f = usbg_get_function(g, 9001, "0"); + assert_null(f); +} + + +/** + * @brief Tests function type translation to string + * @param[in] state Pointer to pointer to correctly initialized test_state structure + * @details Check if get_function_type_str returns proper strings for all types. + */ +static void test_get_function_type_str(void **state) +{ + struct { + usbg_function_type type; + const char *str; + } types[] = { + {F_SERIAL, "gser"}, + {F_ACM, "acm"}, + {F_OBEX, "obex"}, + {F_ECM, "ecm"}, + {F_SUBSET, "geth"}, + {F_NCM, "ncm"}, + {F_EEM, "eem"}, + {F_RNDIS, "rndis"}, + {F_PHONET, "phonet"}, + {F_FFS, "ffs"}, + }; + + const char *str; + int i; + + for (i = 0; i < ARRAY_SIZE(types); i++) { + str = usbg_get_function_type_str(types[i].type); + assert_non_null(str); + assert_string_equal(str, types[i].str); + } +} + +static struct { + usbg_gadget_str code; + const char *name; +} gadget_str_names[] = { + {STR_PRODUCT, "product"}, + {STR_MANUFACTURER, "manufacturer"}, + {STR_SERIAL_NUMBER, "serialnumber"}, +}; + +/** + * @brief Tests gadget codeing name getting + * @param[in] state Pointer to pointer to correctly initialized test_state codeucture + * @details Check if usbg_get_gadget_code_name returns proper codeings for all types. + */ +static void test_get_gadget_str_name(void **state) +{ + const char *name; + int i; + + for (i = 0; i < ARRAY_SIZE(gadget_str_names); i++) { + name = usbg_get_gadget_str_name(gadget_str_names[i].code); + assert_non_null(name); + assert_string_equal(name, gadget_str_names[i].name); + } +} + +/** + * @brief Tests gadget codeing code getting by its name + * @param[in] state Pointer to pointer to correctly initialized test_state codeucture + * @details Check if usbg_lookup_gadget_code returns values matching codeings + */ +static void test_lookup_gadget_str(void **state) +{ + int i, code; + + for (i = 0; i < ARRAY_SIZE(gadget_str_names); i++) { + code = usbg_lookup_gadget_str(gadget_str_names[i].name); + assert_return_code(code, 0); + assert_int_equal(code, gadget_str_names[i].code); + } +} + +/** + * @brief Tests function type translation to string with unknown funcs + * @param[in] state Not used parameter + * @details Check if get_function_type_str returns NULL, when given + * function type is unknown. + */ +static void test_get_function_type_str_fail(void **state) +{ + const char *str; + + str = usbg_get_function_type_str(-1); + assert_null(str); +} + +/** + * @brief Get instance of given function and check it + * @param[in] f Usbg function + * @param[in] tf Test function which should match f + */ +static void try_get_function_instance(usbg_function *f, struct test_function *tf) +{ + const char *str; + + str = usbg_get_function_instance(f); + assert_string_equal(str, tf->instance); +} + +/** + * @brief Tests getting function instance from usbg_function structure + * @param[in] state Pointer to pointer to correctly initialized test_state structure + * @details Check if returned instance name is correct. + */ +static void test_get_function_instance(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_function(ts, s, try_get_function_instance); +} + +/** + * @brief Cpy instance of given usbg function and check it + * @param[in] f Usbg function + * @param[in] tf Test function which should match f + */ +static void try_cpy_function_instance(usbg_function *f, struct test_function *tf) +{ + char str[USBG_MAX_NAME_LENGTH]; + int ret; + int small_len = 2; + + ret = usbg_cpy_function_instance(f, str, USBG_MAX_NAME_LENGTH); + assert_int_equal(ret, USBG_SUCCESS); + assert_string_equal(str, tf->instance); + + ret = usbg_cpy_function_instance(f, str, small_len); + assert_int_equal(ret, USBG_SUCCESS); + assert_memory_equal(str, tf->instance, small_len - 1); + assert_int_equal(str[small_len - 1], '\0'); +} + +/** + * @brief Tests copying function instance from usbg_function structure into buffer + * @param[in] state Pointer to pointer to correctly initialized state + * @details Check if buffer contains expected string + */ +static void test_cpy_function_instance(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_function(ts, s, try_cpy_function_instance); +} + +/** + * @brief Get function type and check it + * @param[in] f Usbg function + * @param[in] tf Test function which should match f by type + */ +static void try_get_function_type(usbg_function *f, struct test_function *tf) +{ + usbg_function_type type; + + type = usbg_get_function_type(f); + assert_int_equal(type, tf->type); +} + +/** + * @brief Tests getting function type + * @details Check if getting function type returns what was expected. + * State must be proper (init must end with success). + * @param[in] state Pointer to pointer to correctly initialized state + */ +static void test_get_function_type(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_function(ts, s, try_get_function_type); +} + +/** + * @brief Check if function instance length is correct + * @param[in] f Usbg function + * @param[in] tf Test function which should match f + */ +static void try_get_function_instance_len(usbg_function *f, struct test_function *tf) +{ + size_t len; + len = usbg_get_function_instance_len(f); + assert_int_equal(len, strlen(tf->instance)); +} + +/** + * @brief Tests getting length of function instance name + * @details Check if returned instance name length matches + * actual length of instance name + * @param[in] state Pointer to pointer to correctly initialized state + */ +static void test_get_function_instance_len(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_function(ts, s, try_get_function_instance_len); +} + +/** + * @brief Tests getting configfs path from usbg state + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_configfs_path(void **state) +{ + usbg_state *s = NULL; + struct test_state *st; + const char *path; + + safe_init_with_state(state, &st, &s); + + path = usbg_get_configfs_path(s); + assert_path_equal(path, st->configfs_path); +} + +/** + * @brief Tests getting configfs path length from usbg state + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_configfs_path_len(void **state) +{ + usbg_state *s = NULL; + struct test_state *st; + int ret, len; + + safe_init_with_state(state, &st, &s); + + ret = usbg_get_configfs_path_len(s); + len = strlen(st->configfs_path); + assert_int_equal(ret, len); +} + +/** + * @brief Tests copying configfs path into buffer + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_cpy_configfs_path(void **state) +{ + usbg_state *s = NULL; + struct test_state *st; + char path[PATH_MAX]; + int ret; + + safe_init_with_state(state, &st, &s); + + ret = usbg_cpy_configfs_path(s, path, PATH_MAX); + assert_int_equal(ret, USBG_SUCCESS); + assert_path_equal(path, st->configfs_path); +} + +/** + * @brief Tests getting config by name + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_config(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, assert_config_equal); +} + +static void test_get_config_without_label(void **state) +{ + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_config *c = NULL; + struct test_state *ts; + struct test_gadget *tg; + struct test_config *tc; + + safe_init_with_state(state, &ts, &s); + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tc = tg->configs; tc->label; tc++) { + c = usbg_get_config(g, tc->id, NULL); + assert_non_null(c); + assert_config_equal(c, tc); + } + } +} + +/** + * @bried Tests getting non-existing config + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_config_fail(void **state) +{ + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_config *c = NULL; + struct test_state *ts; + struct test_gadget *tg; + + safe_init_with_state(state, &ts, &s); + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + + c = usbg_get_config(g, 0, "non-existing-config"); + assert_null(c); + + c = usbg_get_config(g, -9001, "c"); + assert_null(c); + + c = usbg_get_config(g, -9001, NULL); + assert_null(c); + } +} + +/** + * @brief Get config label and check it + * @param[in] c Usbg config + * @param[in] tc Test config which should match c + */ +static void try_get_config_label(usbg_config *c, struct test_config *tc) +{ + const char *label; + label = usbg_get_config_label(c); + assert_string_equal(label, tc->label); +} + +/** + * @brief Tests getting config label + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_config_label(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_get_config_label); +} + +/** + * @brief Check config id with test structure + * @param[in] c Usbg config + * @param[in] tc Test config which should match c + */ +static void try_get_config_id(usbg_config *c, struct test_config *tc) +{ + int id; + id = usbg_get_config_id(c); + assert_int_equal(id, tc->id); +} + +/** + * @brief Tests getting config id + * @param[in,out] state Pointer to pointer to correctly initialized test state. + * When finished, it contains pointer to usbg_state which should be cleaned. + */ +static void test_get_config_id(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_get_config_id); +} + +/** + * @brief Test getting given attributes from gadgets present in state + * @param[in] s Pointer to usbg state + * @param[in] ts Pointer to test state matching given usbg state + * @param[in] attrs Pointer to gadget attributes which should be put in + * virtual filesystem for writting by usbg + */ +static void try_get_gadget_attrs(usbg_state *s, struct test_state *ts, + usbg_gadget_attrs *attrs) +{ + usbg_gadget *g = NULL; + usbg_gadget_attrs actual; + struct test_gadget *tg; + int ret; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + + push_gadget_attrs(tg, attrs); + ret = usbg_get_gadget_attrs(g, &actual); + + assert_int_equal(ret, 0); + assert_gadget_attrs_equal(&actual, attrs); + } +} + +/** + * @brief Tests getting gadget attributes + * @param[in] state Pointer to correctly initialized test_state structure + **/ +static void test_get_gadget_attrs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + + try_get_gadget_attrs(s, ts, &min_gadget_attrs); + try_get_gadget_attrs(s, ts, &max_gadget_attrs); + try_get_gadget_attrs(s, ts, get_random_gadget_attrs()); +} + +/** + * @brief Test setting given attributes on gadgets present in state + * @param[in] s Pointer to usbg state + * @param[in] ts Pointer to test state matching given usbg state + * @param[in] attrs Pointer to gadget attributes to be set + */ +static void try_set_gadget_attrs(usbg_state *s, struct test_state *ts, + usbg_gadget_attrs *attrs) +{ + usbg_gadget *g = NULL; + struct test_gadget *tg; + int ret; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + + pull_gadget_attrs(tg, attrs); + ret = usbg_set_gadget_attrs(g, attrs); + + assert_int_equal(ret, 0); + } +} +/** + * @brief Tests setting gadget attributes + * @param[in] state Pointer to correctly initialized test_state structure + **/ +static void test_set_gadget_attrs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + + try_set_gadget_attrs(s, ts, &min_gadget_attrs); + try_set_gadget_attrs(s, ts, &max_gadget_attrs); + try_set_gadget_attrs(s, ts, get_random_gadget_attrs()); +} + +/** + * @brief Test setting given attributes on gadgets present in state one by one, + * using functions specific for each attribute + * @param[in] s Pointer to usbg state + * @param[in] ts Pointer to test state matching given usbg state + * @param[in] attrs Pointer to gadget attributes to be set + */ +static void try_set_specific_gadget_attr(usbg_state *s, struct test_state *ts, + usbg_gadget_attrs *attrs) +{ + usbg_gadget *g = NULL; + struct test_gadget *tg; + int ret; + int i; + int attr; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + + for (i = USBG_GADGET_ATTR_MIN; i < USBG_GADGET_ATTR_MAX; i++) { + attr = get_gadget_attr(attrs, i); + pull_gadget_attribute(tg, i, attr); + usbg_set_gadget_attr(g, i, attr); + assert_int_equal(ret, 0); + } + } +} + +/** + * @brief Tests setting gadget attributes one by one + * @param[in] state Pointer to correctly initialized test_state structure + **/ +static void test_set_specific_gadget_attr(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + + try_set_specific_gadget_attr(s, ts, &min_gadget_attrs); + try_set_specific_gadget_attr(s, ts, &max_gadget_attrs); + try_set_specific_gadget_attr(s, ts, get_random_gadget_attrs()); +} + +/** + * @brief Tests getting udc from state + * @param[in] state Pointer to correctly initialized test_state structure + **/ +void test_get_udc(void **state) +{ + struct test_state *ts; + char **tu; + struct test_gadget *tg; + usbg_state *s = NULL; + usbg_udc *u = NULL; + usbg_gadget *g = NULL; + + safe_init_with_state(state, &ts, &s); + + for (tu = ts->udcs; *tu; tu++) { + u = usbg_get_udc(s, *tu); + assert_non_null(u); + assert_string_equal(*tu, u->name); + assert_int_equal(s, u->parent); + } + + for (tg = ts->gadgets; tg->name; tg++) { + u = usbg_get_udc(s, tg->udc); + g = usbg_get_gadget(s, tg->name); + assert_int_equal(u->gadget, g); + } +} + +static void test_get_gadget_attr_str(void **state) +{ + struct { + usbg_gadget_attr attr; + const char *str; + } attrs[] = { + {BCD_USB, "bcdUSB"}, + {B_DEVICE_CLASS, "bDeviceClass"}, + {B_DEVICE_SUB_CLASS, "bDeviceSubClass"}, + {B_DEVICE_PROTOCOL, "bDeviceProtocol"}, + {B_MAX_PACKET_SIZE_0, "bMaxPacketSize0"}, + {ID_VENDOR, "idVendor"}, + {ID_PRODUCT, "idProduct"}, + {BCD_DEVICE, "bcdDevice"}, + }; + + const char *str; + int i, j; + + for (i = 0; i < ARRAY_SIZE(attrs); i++) { + str = usbg_get_gadget_attr_str(attrs[i].attr); + assert_non_null(str); + assert_string_equal(str, attrs[i].str); + } + + /* Check if iteration over values works */ + for (i = USBG_GADGET_ATTR_MIN; i < USBG_GADGET_ATTR_MAX; ++i) { + str = usbg_get_gadget_attr_str(i); + assert_non_null(str); + + for (j = 0; j < ARRAY_SIZE(attrs); ++j) + if (attrs[j].attr == i) { + assert_string_equal(str, attrs[j].str); + break; + } + + assert_int_not_equal(j, ARRAY_SIZE(attrs)); + } +} + +static void test_get_gadget_attr_str_fail(void **state) +{ + const char *str; + + str = usbg_get_gadget_attr_str(USBG_GADGET_ATTR_MIN - 1); + assert_null(str); + + str = usbg_get_gadget_attr_str(USBG_GADGET_ATTR_MAX); + assert_null(str); +} + +/** + * @brief set gadget strings + * @details Also do it one by one + * @param[in] data Pointer to correctly initialized test_gadget_strs_data structure + */ +static void test_set_gadget_strs(void **data) +{ + struct test_gadget_strs_data *ts; + struct test_gadget *tg; + usbg_state *s = NULL; + usbg_gadget *g = NULL; + int i; + int ret; + + ts = (struct test_gadget_strs_data *)(*data); + *data = NULL; + + init_with_state(ts->state, &s); + *data = s; + + for (tg = ts->state->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + + pull_gadget_strs(tg, LANG_US_ENG, ts->strs); + ret = usbg_set_gadget_strs(g, LANG_US_ENG, ts->strs); + assert_int_equal(ret, 0); + + for (i = 0; i < GADGET_STR_MAX; i++) + pull_gadget_string(tg, LANG_US_ENG, i, get_gadget_str(ts->strs, i)); + + ret = usbg_set_gadget_serial_number(g, LANG_US_ENG, ts->strs->str_ser); + assert_int_equal(ret, 0); + + ret = usbg_set_gadget_manufacturer(g, LANG_US_ENG, ts->strs->str_mnf); + assert_int_equal(ret, 0); + + ret = usbg_set_gadget_product(g, LANG_US_ENG, ts->strs->str_prd); + assert_int_equal(ret, 0); + + for (i = 0; i < GADGET_STR_MAX; i++) + pull_gadget_string(tg, LANG_US_ENG, i, get_gadget_str(ts->strs, i)); + + + ret = usbg_set_gadget_str(g, STR_SERIAL_NUMBER, LANG_US_ENG, ts->strs->str_ser); + assert_int_equal(ret, 0); + + ret = usbg_set_gadget_str(g, STR_MANUFACTURER, LANG_US_ENG, ts->strs->str_mnf); + assert_int_equal(ret, 0); + + ret = usbg_set_gadget_str(g, STR_PRODUCT, LANG_US_ENG, ts->strs->str_prd); + assert_int_equal(ret, 0); + } +} + +/** + * @brief get gadget strings + * @param[in] data Pointer to correctly initialized test_gadget_strs_data structure + */ +static void test_get_gadget_strs(void **data) +{ + struct test_gadget_strs_data *ts; + struct test_gadget *tg; + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_gadget_strs strs; + + ts = (struct test_gadget_strs_data *)(*data); + *data = NULL; + + init_with_state(ts->state, &s); + *data = s; + + for (tg = ts->state->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + push_gadget_strs(tg, LANG_US_ENG, ts->strs); + usbg_get_gadget_strs(g, LANG_US_ENG, &strs); + assert_gadget_strs_equal(&strs, ts->strs); + } +} + +/** + * @brief Get binding target + * @details Check if given function is target of given binding + * @param[in] tb Test function + * @param[in] b Binding + */ +static void try_get_binding_target(struct test_binding *tb, usbg_binding *b) +{ + usbg_function *f; + + f = usbg_get_binding_target(b); + assert_non_null(f); + assert_func_equal(f, tb->target); +} + +/** + * @brief Test get binding target + * @details Test all bindings present in given state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_get_binding_target(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_binding(ts, s, try_get_binding_target); +} + +/** + * @brief Get binding name + * @details Check if name of given binding is equal name of given function + * @param[in] tb Test function + * @param[in] b Binding + */ +static void try_get_binding_name(struct test_binding *tb, usbg_binding *b) +{ + const char *s; + + s = usbg_get_binding_name(b); + assert_non_null(s); + assert_string_equal(s, tb->name); +} + +/** + * @brief Test get bindig name from all binding present in state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_get_binding_name(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_binding(ts, s, try_get_binding_name); +} + +/** + * @brief Get binding name length + * @param[in] tb Test function + * @param[in] b Binding + */ +static void try_get_binding_name_len(struct test_binding *tb, usbg_binding *b) +{ + int n; + + n = usbg_get_binding_name_len(b); + assert_int_equal(n, strlen(tb->name)); +} + +/** + * @brief Test get binding name length from all bindings present in state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_get_binding_name_len(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_binding(ts, s, try_get_binding_name_len); +} + +/** + * @brief Set config strings + * @param[in] c Config on which string should be set + * @param[in] tc Test config containing strings to be set + */ +static void try_set_config_strs(usbg_config *c, struct test_config *tc) +{ + pull_config_strs(tc, LANG_US_ENG, tc->strs); + usbg_set_config_strs(c, LANG_US_ENG, tc->strs); +} + +/** + * @brief Test setting strings + * @details Set strings in all configs present in state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_set_config_strs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_set_config_strs); +} + +/** + * @brief Set strings one by one on config + * @param[in] c Config on which string should be set + * @param[in] tc Test config containing strings to be set + */ +static void try_set_config_string(usbg_config *c, struct test_config *tc) +{ + pull_config_string(tc, LANG_US_ENG, tc->strs->configuration); + usbg_set_config_string(c, LANG_US_ENG, tc->strs->configuration); +} + +/** + * @brief Test setting strings one by one + * @details Set strings on all configs present in given state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_set_config_string(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_set_config_string); +} + +/** + * @brief Get config strings + * @details Assume that given configs have the same strings + * @param[in] c Config from which strings should be get + * @param[in] tc Test config expected to have the same string as c + */ +static void try_get_config_strs(usbg_config *c, struct test_config *tc) +{ + usbg_config_strs strs; + push_config_strs(tc, LANG_US_ENG, tc->strs); + usbg_get_config_strs(c, LANG_US_ENG, &strs); + assert_string_equal(tc->strs->configuration, strs.configuration); +} + +/** + * @brief Test getting congig strings + * @details Get config strings on all configs present in given state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_get_config_strs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_get_config_strs); +} + +/** + * @brief Get config attributes + * @details Assume that attributes which will be returned are the same as + * given test state contains. + * @param[in] c Usbg config + * @param[in] tc Test config with set attributes + */ +static void try_get_config_attrs(usbg_config *c, struct test_config *tc) +{ + usbg_config_attrs attrs; + + push_config_attrs(tc, tc->attrs); + usbg_get_config_attrs(c, &attrs); + assert_config_attrs_equal(tc->attrs, &attrs); +} + +/** + * @brief Test getting config attributes + * @details Get config attributes on all configfs in state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_get_config_attrs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_get_config_attrs); +} + +/** + * @brief Set config attributes in given config + * @param[in] c Usbg config + * @param[in] tc Test config with attributes which will be set + */ +static void try_set_config_attrs(usbg_config *c, struct test_config *tc) +{ + pull_config_attrs(tc, tc->attrs); + usbg_set_config_attrs(c, tc->attrs); +} + +/** + * @brief Test setting config attributes + * @details Set config attributes on all configs in state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_set_config_attrs(void **state) +{ + usbg_state *s = NULL; + struct test_state *ts; + + safe_init_with_state(state, &ts, &s); + for_each_test_config(ts, s, try_set_config_attrs); +} + +/** + * @brieg Test creating config + * @details Start with empty gadgets, add all functions from given state + * @param[in, out] state Pointer to pointer to correctly initialized test state, + * will point to usbg state when finished. + */ +static void test_create_config(void **state) +{ + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_config *c = NULL; + struct test_state *ts; + struct test_state *empty; + struct test_gadget *tg; + struct test_config *tc; + + ts = (struct test_state *)(*state); + *state = NULL; + + empty = build_empty_gadget_state(ts); + + init_with_state(empty, &s); + *state = s; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tc = tg->configs; tc->label; tc++) { + pull_create_config(tc); + usbg_create_config(g, tc->id, tc->label, + tc->attrs, tc->strs, &c); + assert_config_equal(c, tc); + } + } +} + +/** + * @brief Start with empty gadget, add all functions from given one + */ +static void test_create_function(void **state) +{ + usbg_state *s = NULL; + usbg_gadget *g = NULL; + usbg_function *f = NULL; + struct test_state *ts; + struct test_state *empty; + struct test_gadget *tg; + struct test_function *tf; + + ts = (struct test_state *)(*state); + *state = NULL; + + empty = build_empty_gadget_state(ts); + + init_with_state(empty, &s); + *state = s; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tf = tg->functions; tf->instance; tf++) { + pull_create_function(tf); + usbg_create_function(g, tf->type, tf->instance, + tf->attrs, &f); + assert_func_equal(f, tf); + } + } +} + +/** + * @brief Test only one given function for attribute getting + * @param[in] state Pointer to pointer to correctly initialized state + */ +static void test_get_function_attrs(void **state) +{ + struct test_function_attrs_data *data; + usbg_state *s; + usbg_function *f; + usbg_gadget *g; + usbg_function_attrs actual; + int ret; + + data = (struct test_function_attrs_data *)(*state); + *state = NULL; + + + init_with_state(data->state, &s); + *state = s; + + g = usbg_get_first_gadget(s); + assert_non_null(g); + f = usbg_get_first_function(g); + assert_non_null(f); + + push_function_attrs(&data->state->gadgets[0].functions[0], data->attrs); + ret = usbg_get_function_attrs(f, &actual); + + assert_int_equal(ret, 0); + assert_function_attrs_equal(&actual, data->attrs, data->attrs->header.attrs_type); + + usbg_cleanup_function_attrs(&actual); +} + +/** + * @brief Test setting attributes in only one given function + * @param[in] state Pointer to pointer to correctly initialized state + */ +static void test_set_function_attrs(void **state) +{ + struct test_function_attrs_data *data; + usbg_state *s; + usbg_function *f; + usbg_gadget *g; + int ret; + + data = (struct test_function_attrs_data *)(*state); + *state = NULL; + + init_with_state(data->state, &s); + *state = s; + + g = usbg_get_first_gadget(s); + assert_non_null(g); + f = usbg_get_first_function(g); + assert_non_null(f); + + pull_function_attrs(&data->state->gadgets[0].functions[0], data->attrs); + ret = usbg_set_function_attrs(f, data->attrs); + + assert_int_equal(ret, 0); +} + +/** + * + * @brief cleanup usbg state + */ +static int teardown_state(void **state) +{ + usbg_state *s = NULL; + + s = (usbg_state *)(*state); + if (s != NULL) + usbg_cleanup(s); + + cleanup_stack(); + return 0; +} + +/* Custom macro for defining test with given name and fixed teardown function */ +#define USBG_TEST_TS(name, test, setup) \ + USBG_TEST(name, test, setup, teardown_state) + +/** + * @page usbg_tests Tests + * @brief This is list of test cases + * @tests_start + */ + +#ifndef DOXYGEN +static struct CMUnitTest tests[] = { +#endif + + /** + * @usbg_test + * @test_desc{test_init_simple, + * Check if init was successfull on simple configfs state, + * usbg_init} + */ + USBG_TEST_TS("test_init_simple", + test_init, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_init_all_funcs, + * Check if init was successfull when all avaible functions + * are present in configfs, + * usbg_init} + */ + USBG_TEST_TS("test_init_all_funcs", + test_init, setup_all_funcs_state), + /** + * @usbg_test + * @test_desc{test_init_long_path, + * Try to initialize libusbg with long configfs path, + * usbg_init} + */ + USBG_TEST_TS("test_init_long_path", + test_init, setup_long_path_state), + /** + * @usbg_test + * @test_desc{test_init_long_udc, + * Try to initialize libusbg with long udc name, + * usbg_init} + */ + USBG_TEST_TS("test_init_long_udc", + test_init, setup_long_udc_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_simple, + * Check if simple gadget will be correcty returned, + * usbg_get_gadget} + */ + USBG_TEST_TS("test_get_gadget_simple", + test_get_gadget, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_fail_simple, + * Check if getting non-existing and wrong gadgets cause + * expected failure and error codes are correct, + * usbg_get_gadget} + */ + USBG_TEST_TS("test_get_gadget_fail_simple", + test_get_gadget_fail, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_first_gadget_simple, + * Check if gadget returned by get_first_gadget + * is actually first one, + * usbg_get_first_gadget} + */ + USBG_TEST_TS("test_get_first_gadget_simple", + test_get_first_gadget, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_first_gadget_fail, + * Check if getting first gadget from state returns NULL when + * invalid parameters are passed, + * usbg_get_first_gadget} + */ + unit_test(test_get_first_gadget_fail), + /** + * @usbg_test + * @test_desc{test_get_gadget_name_simple, + * Check if returned gadget name matches expected value, + * usbg_get_gadget_name} + */ + USBG_TEST_TS("test_get_gadget_name_simple", + test_get_gadget_name, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_name_len, + * Check if returned simple gadget name length matches expected value, + * usbg_get_gadget_name} + */ + USBG_TEST_TS("test_get_gadget_name_len_simple", + test_get_gadget_name_len, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_name_fail, + * Check if trying to get name of invalid gadget + * cause expected failure (name is null), + * usbg_get_gadget_name} + */ + unit_test(test_get_gadget_name_fail), + /** + * @usbg_test + * @test_desc{test_cpy_gadget_name_simple, + * Check if getting simple gadget name into buffer work as expected, + * usbg_cpy_gadget_name} + */ + USBG_TEST_TS("test_cpy_gadget_name_simple", + test_cpy_gadget_name, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_cpy_gadget_name_fail_simple, + * Check if writting gadget name into buffer fail when + * invalid parameters are passed, + * usbg_cpy_gadget_name} + */ + USBG_TEST_TS("test_cpy_gadget_name_fail_simple", + test_cpy_gadget_name_fail, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_function_simple, + * Check if function can be correctly get from simple state, + * usbg_get_function} + */ + USBG_TEST_TS("test_get_function_simple", + test_get_function, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_function_all_funcs, + * Check if getting function work on all function types, + * usbg_get_function} + */ + USBG_TEST_TS("test_get_function_all_funcs", + test_get_function, setup_all_funcs_state), + /** + * @usbg_test + * @test_desc{test_get_function_same_type_funcs, + * Check if having multiple functions with the same type does not + * cause failure + * usbg_get_function} + */ + USBG_TEST_TS("test_get_function_same_type_funcs", + test_get_function, setup_same_type_funcs_state), + /** + * @usbg_test + * @test_desc{test_get_function_fail_simple, + * Check if trying to get invalid function's name ends + * with expected error, + * usbg_get_function} + */ + USBG_TEST_TS("test_get_function_fail_simple", + test_get_function_fail, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_function_instance_simple, + * Check if getting simple instance returns what expected, + * usbg_get_function_instance} + */ + USBG_TEST_TS("test_get_function_instance_simple", + test_get_function_instance, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_cpy_function_instance_simple, + * Check if copying simple instance into buffer returns what expected, + * usbg_cpy_function_instance} + */ + USBG_TEST_TS("test_cpy_function_instance_simple", + test_cpy_function_instance, setup_all_funcs_state), + /** + * @usbg_test + * @test_desc{test_get_function_type_simple, + * Check if function type is returned correctly, + * usbg_get_function_type} + */ + USBG_TEST_TS("test_get_function_type_simple", + test_get_function_type, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_function_type_all_funcs, + * Check if all function types are returned correctly, + * usbg_get_function_type} + */ + USBG_TEST_TS("test_get_function_type_all_funcs", + test_get_function_type, setup_all_funcs_state), + /** + * @usbg_test + * @test_desc{test_get_function_instance_len_simple, + * Check if function instance length is returned correctly, + * usbg_get_function_instance_len} + */ + USBG_TEST_TS("test_get_function_instance_len_simple", + test_get_function_instance_len, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_function_type_str, + * Compare returned function types strings with expected values, + * usbg_get_function_type_str} + */ + unit_test(test_get_function_type_str), + /** + * @usbg_test + * @test_desc{test_get_function_type_str_fail, + * Try to get type string of unknown type, + * usbg_get_function_type_str} + */ + unit_test(test_get_function_type_str_fail), + /** + * @usbg_test + * @test_desc{test_get_configfs_path_simple, + * heck if simple configfs path was returned correctly, + * usbg_get_configfs_path} + */ + USBG_TEST_TS("test_get_configfs_path_simple", + test_get_configfs_path, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_configfs_path_len, + * Check if configfs path length is correctly calculated, + * usbg_get_configfs_path_len} + */ + USBG_TEST_TS("test_get_configfs_path_len_simple", + test_get_configfs_path_len, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_cpy_configfs_path_simple, + * Copy simple configfs path into buffer and compare with original, + * usbg_cpy_configfs_path} + */ + USBG_TEST_TS("test_cpy_configfs_path_simple", + test_cpy_configfs_path, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_config_simple, + * Check if returned simple config matches original one, + * usbg_get_config} + */ + USBG_TEST_TS("test_get_config_simple", + test_get_config, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_config_without_label_simple, + * Check if returned simple config matches original one, + * usbg_get_config} + */ + USBG_TEST_TS("test_get_config_without_label_simple", + test_get_config_without_label, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_config_fail, + * Check if trying to get non-existing or invalid config + * fails as expected, + * usbg_get_config}*/ + USBG_TEST_TS("test_get_config_fail", + test_get_config_fail, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_config_label_simple, + * Check if returned simple config label matches original one, + * usbg_get_config_label} + */ + USBG_TEST_TS("test_get_config_label_simple", + test_get_config_label, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_config_id_simple, + * Check if returned simple config id matches original one, + * usbg_get_config_id} + */ + USBG_TEST_TS("test_get_config_id_simple", + test_get_config_id, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_attrs_simple, + * Get gadget attributes list and compare them with original, + * usbg_get_gadget_attrs} + */ + USBG_TEST_TS("test_get_gadget_attrs_simple", + test_get_gadget_attrs, setup_simple_state), + /** + * @usbg_tets + * @test_desc{test_set_gadget_attrs_simple, + * Set gadget attributes list\, check if everything is wrote + * as expected, + * usbg_set_gadget_attrs} + */ + USBG_TEST_TS("test_set_gadget_attrs_simple", + test_set_gadget_attrs, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_set_specific_gadget_attr_simple, + * Set gadget attributes one by one, + * usbg_set_gadget_attrs} + */ + USBG_TEST_TS("test_set_specific_gadget_attr_simple", + test_set_specific_gadget_attr, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_udc_simple, + * Get udc name from state, + * usbg_get_udc} + */ + USBG_TEST_TS("test_get_udc_simple", + test_get_udc, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_udc_long, + * Get udc name witch very long name, + * usbg_get_udc} + */ + USBG_TEST_TS("test_get_udc_long", + test_get_udc, setup_long_udc_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_attr_str, + * Compare returned gadget attribute strings witch expected values + * usbg_get_gadget_attr_str} + */ + unit_test(test_get_gadget_attr_str), + /** + * @usbg_test + * @test_desc{test_get_gadget_attr_str_fail, + * Check returned gadget attribute strings for invalid arguments + * usbg_get_gadget_attr_str} + */ + unit_test(test_get_gadget_attr_str_fail), + /** + * @usbg_test + * @test_desc{test_set_gadget_strs_random, + * Set gadget strings of random length, + * usbg_set_gadget_strs} + */ + USBG_TEST_TS("test_set_gadget_strs_random", + test_set_gadget_strs, setup_random_len_gadget_strs_data), + /** + * @usbg_test + * @test_desc{test_get_gadget_strs_random, + * Get gadget strings, + * usbg_get_gadget_strs} + */ + USBG_TEST_TS("test_get_gadget_strs_random", + test_get_gadget_strs, setup_random_len_gadget_strs_data), + /** + * @usbg_test + * @test_desc{test_get_binding_target_simple, + * Get binding target, + * usbg_get_binding_target} + */ + USBG_TEST_TS("test_get_binding_target_simple", + test_get_binding_target, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_binding_name_simple, + * Get binding name, + * usbg_get_binding_name} + */ + USBG_TEST_TS("test_get_binding_name_simple", + test_get_binding_name, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_get_binding_name_len_simple, + * Get binding name length, + * usbg_get_binding_name_len} + */ + USBG_TEST_TS("test_get_binding_name_len_simple", + test_get_binding_name_len, setup_simple_state), + /** + * @usbg_test + * @test_desc{test_set_config_strs_simple, + * Set simple strings in set of configurations, + * usbg_set_config_strs} + */ + USBG_TEST_TS("test_set_config_strs_simple", + test_set_config_strs, setup_simple_config_strs_state), + /** + * @usbg_test + * @test_desc{test_set_config_string_simple, + * Set simple string in set of configurations, + * usbg_set_config_string} + */ + USBG_TEST_TS("test_set_config_string_simple", + test_set_config_string, setup_simple_config_strs_state), + /** + * @usbg_test + * @test_desc{test_get_config_strs_simple, + * Get simple strings from set of configurations, + * usbg_get_config_strs} + */ + USBG_TEST_TS("test_get_config_strs_simple", + test_get_config_strs, setup_simple_config_strs_state), + /** + * @usbg_test + * @test_desc{test_get_config_attrs_max, + * Get config attributes with max values, + * usbg_get_config_attrs} + */ + USBG_TEST_TS("test_get_config_attrs_max", + test_get_config_attrs, setup_max_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_get_config_attrs_min, + * Get config attributes with minimum values, + * usbg_get_config_attrs} + */ + USBG_TEST_TS("test_get_config_attrs_min", + test_get_config_attrs, setup_min_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_get_config_attrs_random, + * Get config attributes with random values, + * usbg_get_config_attrs} + */ + USBG_TEST_TS("test_get_config_attrs_random", + test_get_config_attrs, setup_random_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_set_config_attrs_max, + * Set config attributes with max values, + * usbg_set_config_attrs} + */ + USBG_TEST_TS("test_set_config_attrs_max", + test_set_config_attrs, setup_max_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_set_config_attrs_min, + * Set config attributes with minimum values, + * usbg_set_config_attrs} + */ + USBG_TEST_TS("test_set_config_attrs_min", + test_set_config_attrs, setup_min_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_set_config_attrs_random, + * Set config attributes with random values, + * usbg_set_config_attrs} + */ + USBG_TEST_TS("test_set_config_attrs_random", + test_set_config_attrs, setup_random_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_create_config_random, + * Create config with random attributes + * usbg_create_config} + */ + USBG_TEST_TS("test_create_config_random", + test_create_config, setup_random_config_attrs_state), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Get f_serial function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_serial_attrs", + test_get_function_attrs, setup_f_serial_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_obex_attrs, + * Get f_obex function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_obex_attrs", + test_get_function_attrs, setup_f_obex_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_acm_attrs, + * Get f_acm function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_acm_attrs", + test_get_function_attrs, setup_f_acm_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_ecm_attrs, + * Get f_ecm function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_ecm_attrs", + test_get_function_attrs, setup_f_ecm_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_eem_attrs, + * Get f_eem function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_eem_attrs", + test_get_function_attrs, setup_f_eem_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_subset_attrs, + * Get f_subset function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_subset_attrs", + test_get_function_attrs, setup_f_subset_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_ncm_attrs, + * Get f_ncm function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_ncm_attrs", + test_get_function_attrs, setup_f_ncm_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Get f_rndis function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_rndis_attrs", + test_get_function_attrs, setup_f_rndis_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_phonet_attrs, + * Get f_phonet function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_phonet_attrs", + test_get_function_attrs, setup_f_phonet_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Get f_ffs function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_get_f_ffs_attrs", + test_get_function_attrs, setup_f_ffs_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Set f_serial function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_serial_attrs", + test_set_function_attrs, setup_f_serial_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_acm_attrs, + * Set f_acm function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_acm_attrs", + test_set_function_attrs, setup_f_acm_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_obex, + * Set f_obex function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_obex_attrs", + test_set_function_attrs, setup_f_obex_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_ecm_attrs, + * Set f_ecm function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_ecm_attrs", + test_set_function_attrs, setup_f_ecm_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_eem_attrs, + * Set f_eem function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_eem_attrs", + test_set_function_attrs, setup_f_eem_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_subset_attrs, + * Set f_subset function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_subset_attrs", + test_set_function_attrs, setup_f_subset_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_ncm_attrs, + * Set f_ncm function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_ncm_attrs", + test_set_function_attrs, setup_f_ncm_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Set f_rndis function attributes, + * usbg_get_function_attrs} + */ + USBG_TEST_TS("test_set_f_rndis_attrs", + test_set_function_attrs, setup_f_rndis_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_phonet_attrs, + * Set f_phonet function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_phonet_attrs", + test_set_function_attrs, setup_f_phonet_writable_attrs), + /** + * @usbg_test + * @test_desc{test_get_f_serial_attrs, + * Set f_ffs function attributes, + * usbg_set_function_attrs} + */ + USBG_TEST_TS("test_set_f_ffs_attrs", + test_set_function_attrs, setup_f_ffs_writable_attrs), + /** + * @usbg_test + * @test_desc{test_create_all_functions, + * Create full set of functions in empty state, + * usbg_get_binding_name_len} + */ + USBG_TEST_TS("test_create_all_functions", + test_create_function, setup_all_funcs_state), + /** + * @usbg_test + * @test_desc{test_get_gadget_str_name, + * Compare returned gadget string name with expected + * usbg_get_gadget_str_name} + */ + unit_test(test_get_gadget_str_name), + /** + * @usbg_test + * @test_desc{test_lookup_gadget_str, + * Compare returned gadget string code with expected + * usbg_lookup_gadget_str} + */ + unit_test(test_lookup_gadget_str), + +#ifndef DOXYGEN +}; +#endif + +/** + * @usbg_test + * @tests_end + */ + +#define TESTS_TAG "tests" +/* for autotools compability */ +#define SKIPPED_CODE 77 + +#ifdef HAS_LIBCONFIG + +static int gen_test_config(FILE *output) +{ + config_t cfg; + config_setting_t *root; + config_setting_t *tests_node, *node; + int i; + int ret = SKIPPED_CODE, cfg_ret = 0; + + config_init(&cfg); + config_set_tab_width(&cfg, 4); + + root = config_root_setting(&cfg); + tests_node = config_setting_add(root, TESTS_TAG, CONFIG_TYPE_LIST); + if (!tests_node) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + node = config_setting_add(tests_node, NULL, CONFIG_TYPE_STRING); + if (!node) { + ret = -ENOMEM; + goto out; + } + + cfg_ret = config_setting_set_string(node, tests[i].name); + if (cfg_ret != CONFIG_TRUE) { + ret = -EINVAL; + goto out; + } + } + + config_write(&cfg, output); +out: + config_destroy(&cfg); + return ret; +} + +#else + +static int gen_test_config(FILE *output) +{ + fprintf(stderr, "Libconfig is not supported\n"); + return -ENOTSUP; +} + +#endif /* HAS_LIBCONFIG */ + +static int lookup_test(const char *name) +{ + int i; + for (i = 0; i < ARRAY_SIZE(tests); ++i) + if (!strcmp(name, tests[i].name)) + return i; + + return -1; +} + +static void test_skipped(void **state) +{ + skip(); +} + +#ifdef HAS_LIBCONFIG +static int apply_test_config(FILE *input) +{ + config_t cfg; + config_setting_t *root; + config_setting_t *tests_node, *node; + int i, count, ind; + int ret = 0, cfg_ret = 0; + const char *test_name; + char selected[ARRAY_SIZE(tests)]; + + for (i = 0; i < ARRAY_SIZE(selected); ++i) + selected[i] = 0; + + config_init(&cfg); + + cfg_ret = config_read(&cfg, input); + if (cfg_ret != CONFIG_TRUE) { + fprintf(stderr, "Wrong config format\n"); + ret = -EINVAL; + goto out; + } + + root = config_root_setting(&cfg); + tests_node = config_setting_get_member(root, TESTS_TAG); + if (!tests_node || !config_setting_is_list(tests_node)) { + fprintf(stderr, "Missing or incorrect tests list\n"); + ret = -EINVAL; + goto out; + } + + count = config_setting_length(tests_node); + for (i = 0; i < count; ++i) { + node = config_setting_get_elem(tests_node, i); + if (!node) { + ret = -EINVAL; + goto out; + } + + test_name = config_setting_get_string(node); + if (!test_name) { + fprintf(stderr, "Incorrect tests list. Element %d\n", i); + ret = -EINVAL; + goto out; + } + + ind = lookup_test(test_name); + if (ind < 0) { + fprintf(stderr, "Test %s not found.\n", test_name); + ret = -EINVAL; + goto out; + } + + selected[ind] = 1; + } + + for (i = 0; i < ARRAY_SIZE(selected); ++i) { + if (selected[i]) + continue; + + tests[i].test_func = &test_skipped; + tests[i].setup_func = NULL; + tests[i].teardown_func = NULL; + } +out: + config_destroy(&cfg); + return ret; +} + +#else + +static int apply_test_config(FILE *input) +{ + fprintf(stderr, "Libconfig is not supported\n"); + return -ENOTSUP; +} + +#endif /* HAS_LIBCONFIG */ + +static void print_help() +{ + fprintf(stderr, + "libusbgx test suit:\n" + " --generate-config - generates config to stdout and exit\n" + " --use-config - runs test suit using config from stdin\n" + " -h --help - print this message\n" + ); +} + +int main(int argc, char **argv) +{ + enum { + GENERATE_CONFIG = 0x01, + USE_CONFIG = 0x02, + }; + + int options = 0; + int opt; + int ret = -EINVAL; + + static struct option long_options[] = { + {"generate-config", no_argument, 0, 1}, + {"use-config", no_argument, 0, 2}, + {"help", no_argument, 0, 'h'}, + {NULL, 0, 0, 0} + }; + + while (1) { + opt = getopt_long(argc, argv, "h", long_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 1: + options |= GENERATE_CONFIG; + break; + case 2: + options |= USE_CONFIG; + break; + case 'h': + default: + print_help(); + goto out; + } + } + + if (optind < argc || + ((options & GENERATE_CONFIG) && + (options & USE_CONFIG))) { + print_help(); + goto out; + } + + if (options & GENERATE_CONFIG) { + ret = gen_test_config(stdout); + goto out; + } + + if (options & USE_CONFIG) { + ret = apply_test_config(stdin); + if (ret) + goto out; + } + + ret = cmocka_run_group_tests(tests, NULL, NULL); + +out: + return ret; +} diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000..45b8e3e --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +#USE_CONFIG=0 +#GENERATE_CONFIG=0 +#HELP=$HELP + +# for autotools compability (config can be passed by environment variable) +if [[ -n $USE_CONFIG ]] +then + CONFIG_FILE=$USE_CONFIG +elif [[ -n $GENERATE_CONFIG ]] +then + CONFIG_FILE=$GENERATE_CONFIG +fi + +function usage { + echo "libusbgx test suit" + echo "Usage: ./test.sh [option]" + echo "Options:" + echo " --generate-config filename - generates config to given file and exit" + echo " --use-config filename - runs test suit using config from given file" + echo " -h --help - print this message" +} + +# Parse arguments + +ARGS=$(getopt --long generate-config:,use-config:,help -o h -- "$@" ) + +if [ $? -ne 0 ] +then + HELP=1 +fi + +eval set -- $ARGS + +while true; do + case $1 in + -h|--help) + HELP=1 + shift + ;; + --use-config) + USE_CONFIG=1 + CONFIG_FILE=$2 + shift 2 + ;; + --generate-config) + GENERATE_CONFIG=1 + CONFIG_FILE=$2 + shift 2 + ;; + --) + shift + break + ;; + *) + HELP=1 + shift + ;; + esac +done + +# Run test with io functions ovverride + +if [[ -n $USE_CONFIG ]] +then + LD_LIBRARY_PATH=. ./test --use-config < $CONFIG_FILE +elif [[ -n $GENERATE_CONFIG ]] +then + LD_LIBRARY_PATH=. ./test --generate-config > $CONFIG_FILE +elif [[ -n $HELP ]] +then + usage + exit 77 # autotools consider it skipped +else + LD_LIBRARY_PATH=. ./test +fi + diff --git a/tests/usbg-io-wrappers.c b/tests/usbg-io-wrappers.c new file mode 100644 index 0000000..d8f471a --- /dev/null +++ b/tests/usbg-io-wrappers.c @@ -0,0 +1,203 @@ +#define _GNU_SOURCE +#include <dirent.h> +#include <stdio.h> +#include <stdarg.h> +#include <setjmp.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <cmocka.h> +#include <dlfcn.h> +#include <errno.h> + +typedef int (*fputs_f_type)(const char *, FILE *); +typedef int (*fflush_f_type)(FILE *); +typedef fflush_f_type ferror_f_type; + +/** + * @brief Simulates opening file + * @details Checks if path is equal expected value and returns given pointer + * from cmocka queue + */ +FILE *fopen(const char *path, const char *mode) +{ + check_expected(path); + return mock_ptr_type(FILE *); +} + +/** + * @brief Simulates closing file + * @details Does absolutely nothing, always acts as successfull close + */ +int fclose(FILE *fp) +{ + check_expected(fp); + return mock_type(int); +} + +/** + * @brief Simulates reading file + * @details Does not read any file, instead returns value from cmocka queue + * @return value specified by caller previously + */ +char *fgets(char *s, int size, FILE *stream) +{ + check_expected(stream); + strncpy(s, mock_ptr_type(char *), size); + return s; +} + +/** + * @brief Simulates opening directory + * @details Does not open any dir, instead returns user-specified value + * @return value specified by caller previously + */ +DIR *opendir(const char *name) +{ + int err; + + check_expected(name); + err = mock_type(int); + if (err) + errno = err; + + return mock_ptr_type(DIR *); +} + +/** + * @brief Simulates closing directory + * @details Does nothing and ends successfully. + */ +int closedir(DIR *dirp) +{ + check_expected(dirp); + return mock_type(int); +} + +/** + * @brief Simulates scanning directory + * @details Checks if dirp has expected value. Then consecutive values from + * cmocka queue are proceed. First value must be integer and indicates number + * of directory entries which should be returned. Next number of values indicate + * names of directory entries. + */ +int scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) +{ + int count; + int i, j = 0; + char *name; + struct dirent **entries; + struct dirent *entry; + int tmp, expected; + + check_expected(dirp); + count = mock_type(int); + + if (count > 0) + entries = calloc(count, sizeof(*entries)); + else + entries = NULL; + + for (i = 0; i < count; i++) { + name = mock_ptr_type(char *); + entry = malloc(sizeof(*entry)); + if (strlen(name) >= NAME_MAX) + fail(); + + strcpy(entry->d_name, name); + entry->d_type = mock_type(unsigned char); + + expected = mock_type(int); + if (filter) { + tmp = filter(entry); + assert_int_equal(tmp, expected); + if (tmp) + entries[j++] = entry; + else + free(entry); + } + } + + if (compar) + qsort(entries, count, sizeof(*entries), + (int (*)(const void *,const void *))compar); + + *namelist = entries; + return j; +} + +/** + * @brief Simultes readlink, with user-specified behavior + * @datails Check if path and bufsiz equal expedted values and + * write to buf string given by cmocka + */ +ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + char *res; + int reslen; + + check_expected(path); + check_expected(bufsiz); + res = mock_ptr_type(char *); + reslen = strlen(res); + if (bufsiz <= reslen) + fail(); + + strcpy(buf, res); + + return reslen; +} + +/** + * @brief Simulates puts, with user-specified behavior + * @details Check if user is trying to write expected data + * @return value received from cmocka queue + */ +int fputs(const char *s, FILE *stream) +{ + /* Cmocka (or anything else) may want to print some errors. + * Especially when running fputs itself */ + if (stream == stderr || stream == stdout) { + fputs_f_type orig_fputs; + orig_fputs = (fputs_f_type)dlsym(RTLD_NEXT, "fputs"); + return orig_fputs(s, stream); + } + + check_expected(stream); + check_expected(s); + return mock_type(int); +} + +int mkdir(const char *pathname, mode_t mode) +{ + check_expected(pathname); + check_expected(mode); + return mock_type(int); +} + +/** + * @brief Does nothing. + */ +int fflush(FILE *stream) +{ + if (stream == stderr || stream == stdout) { + fflush_f_type orig_fflush; + orig_fflush = (fflush_f_type)dlsym(RTLD_NEXT, "fflush"); + return orig_fflush(stream); + } + + return 0; +} + +int ferror(FILE *stream) +{ + if (stream == stderr || stream == stdout) { + ferror_f_type orig_ferror; + orig_ferror = (ferror_f_type)dlsym(RTLD_NEXT, "ferror"); + return orig_ferror(stream); + } + + return 0; +} diff --git a/tests/usbg-test.c b/tests/usbg-test.c new file mode 100644 index 0000000..c332795 --- /dev/null +++ b/tests/usbg-test.c @@ -0,0 +1,1389 @@ +#include <usbg/usbg.h> +#include <stdio.h> +#include <stdarg.h> +#include <setjmp.h> +#include <cmocka.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <limits.h> +#include <errno.h> +#include <time.h> + +#include "usbg-test.h" + +static struct simple_stack{ + void *ptr; + struct simple_stack *next; +} *cleanup_top = NULL; + +static const char *gadget_str_names[] = { + "serialnumber", + "manufacturer", + "product" +}; + +static const char *config_attr_names[] = { + "MaxPower", + "bmAttributes" +}; + +static attr_format config_attr_format[] = { + [MAX_POWER] = FORMAT_DEC, + [BM_ATTRIBUTES] = FORMAT_HEX +}; + +void free_later(void *ptr) +{ + struct simple_stack *t; + + t = malloc(sizeof(*t)); + t->ptr = ptr; + t->next = cleanup_top; + cleanup_top = t; +} + +void cleanup_stack() +{ + struct simple_stack *t; + + while (cleanup_top) { + free(cleanup_top->ptr); + t = cleanup_top->next; + free(cleanup_top); + cleanup_top = t; + } +} + +/* Represent last file/dir opened, next should have bigger numbers.*/ +static int file_id = 0; +static int dir_id = 0; + +#define PUSH_FILE(file, content) do {\ + file_id++;\ + expect_path(fopen, path, file);\ + will_return(fopen, file_id);\ + expect_value(fgets, stream, file_id);\ + will_return(fgets, content);\ + expect_value(fclose, fp, file_id);\ + will_return(fclose, 0);\ +} while(0) + +#define PUSH_FILE_ALWAYS(dflt) do {\ + expect_any_count(fopen, path, -1);\ + will_return_always(fopen, 1);\ + expect_any_count(fgets, stream, 1);\ + will_return_always(fgets, dflt);\ + expect_any_count(fclose, fp, 1);\ + will_return_always(fclose, 0);\ +} while(0) + +#define PUSH_EMPTY_DIR(p) do {\ + expect_string(scandir, dirp, p);\ + will_return(scandir, 0);\ +} while(0) + +#define EXPECT_OPENDIR(n) do {\ + dir_id++;\ + expect_path(opendir, name, n);\ + will_return(opendir, 0);\ + will_return(opendir, dir_id);\ + expect_value(closedir, dirp, dir_id);\ + will_return(closedir, 0);\ +} while(0) + +#define EXPECT_OPENDIR_ERROR(n, e) do {\ + expect_path(opendir, name, n);\ + will_return(opendir, e);\ + will_return(opendir, NULL);\ +} while(0) + +#define PUSH_DIR(p, c) do {\ + expect_path(scandir, dirp, p);\ + will_return(scandir, c);\ +} while(0) + +#define PUSH_DIR_ENTRY(name, type) do {\ + will_return(scandir, name);\ + will_return(scandir, type);\ + will_return(scandir, 1);\ +} while(0) + +#define PUSH_LINK(p, c, len) do {\ + expect_path(readlink, path, p);\ + expect_in_range(readlink, bufsiz, len, INT_MAX);\ + will_return(readlink, c);\ +} while(0) + +#define EXPECT_WRITE(file, content) do {\ + file_id++;\ + expect_path(fopen, path, file);\ + will_return(fopen, file_id);\ + expect_value(fputs, stream, file_id);\ + expect_string(fputs, s, content);\ + will_return(fputs, 0);\ + expect_value(fclose, fp, file_id);\ + will_return(fclose, 0);\ +} while(0) + +#define EXPECT_HEX_WRITE(file, content) do {\ + file_id++;\ + expect_path(fopen, path, file);\ + will_return(fopen, file_id);\ + expect_value(fputs, stream, file_id);\ + expect_check(fputs, s, hex_str_equal_display_error, content);\ + will_return(fputs, 0);\ + expect_value(fclose, fp, file_id);\ + will_return(fclose, 0);\ +} while(0) + +#define EXPECT_MKDIR(p) do {\ + expect_path(mkdir, pathname, p);\ + expect_value(mkdir, mode, 00777);\ + will_return(mkdir, 0);\ +} while(0) + +/** + * @brief Compare test gadgets' names + */ +static int test_gadget_cmp(struct test_gadget *a, struct test_gadget *b) +{ + return strcoll(a->name, b->name); +} + +/** + * @brief Compare test functions' names + */ +static int test_function_cmp(struct test_function *a, struct test_function *b) +{ + return strcoll(a->name, b->name); +} + +/** + * @brief Compare test bindings' names + */ +static int test_binding_cmp(struct test_binding *a, struct test_binding *b) +{ + return strcoll(a->name, b->name); +} + +/** + * @brief Compare test configs' names + */ +static int test_config_cmp(struct test_config *a, struct test_config *b) +{ + return strcoll(a->name, b->name); +} + +void prepare_binding(struct test_binding *b, struct test_function *f, char *fpath) +{ + if (!f->name) + prepare_function(f, fpath); + + if (!b->name) { + b->name = strdup(f->name); + if (b->name == NULL) + fail(); + free_later(b->name); + } + + b->target = f; +} + +void prepare_config(struct test_config *c, char *cpath, char *fpath) +{ + int count = 0; + struct test_function *f; + struct test_binding *b; + int i; + + safe_asprintf(&c->name, "%s.%d", c->label, c->id); + + c->path = cpath; + + /* check if bindings has been already filled */ + if (!c->bindings) { + for (f = c->bound_funcs; f->instance; f++) + count++; + + c->bindings = safe_calloc(count + 1, sizeof(*c->bindings)); + } else { + for (b = c->bindings; b->name; b++) + count++; + } + + for (i = 0; i < count; i++) + prepare_binding(&c->bindings[i], &c->bound_funcs[i], fpath); + + qsort(c->bindings, count, sizeof(*c->bindings), + (int (*)(const void *, const void *))test_binding_cmp); + +} + +void prepare_function(struct test_function *f, char *path) +{ + const char *func_type; + + func_type = usbg_get_function_type_str(f->type); + if (func_type == NULL) + fail(); + + safe_asprintf(&f->name, "%s.%s", func_type, f->instance); + + f->path = path; +} + +void prepare_gadget(struct test_state *state, struct test_gadget *g) +{ + struct test_config *c; + struct test_function *f; + char *fpath; + char *cpath; + int count; + + g->path = strdup(state->path); + if (!g->path) + fail(); + + free_later(g->path); + + safe_asprintf(&fpath, "%s/%s/functions", g->path, g->name); + + count = 0; + for (f = g->functions; f->instance; f++) { + prepare_function(f, fpath); + count++; + } + + /* Path needs to be known somehow when list is empty */ + f->path = fpath; + + qsort(g->functions, count, sizeof(*g->functions), + (int (*)(const void *, const void *))test_function_cmp); + + safe_asprintf(&cpath, "%s/%s/configs", g->path, g->name); + + count = 0; + for (c = g->configs; c->label; c++) { + prepare_config(c, cpath, fpath); + count++; + } + + /* Path needs to be known somehow when list is empty */ + c->path = cpath; + + qsort(g->configs, count, sizeof(*g->configs), + (int (*)(const void *, const void *))test_config_cmp); + +} + +static void cpy_test_function(struct test_function *to, + struct test_function *from) +{ + /* Reuse instance */ + to->instance = from->instance; + to->type = from->type; + /* path and name is not being copied because + it has not been allocated now */ + + to->writable = 1; +} + +static struct test_function *dup_test_functions(struct test_function *functions) +{ + struct test_function *f, *nf, *new_functions; + int count = 0; + + for (f = functions; f->instance; ++f) + ++count; + + new_functions = safe_calloc(count + 1, sizeof(*f)); + + for (f = functions, nf = new_functions; f->instance; ++f, ++nf) + cpy_test_function(nf, f); + + return new_functions; +} + +static struct test_function *get_new_binding_target(struct test_function *which, + struct test_function *old, + int count, + struct test_function *new) +{ + struct test_function *ret = NULL; + + /* Should duplicate function? */ + if (which < old || ((which - old) > count)) { + /* We may need to do a deep copy */ + if (!which->writable) { + ret = safe_calloc(1, sizeof(*ret)); + cpy_test_function(ret, which); + } else { + ret = which; + } + } else if (old != new) { + /* Function has been copied in bound_funcs so just + set new address */ + ret = which - old + new; + } else { + /* Functions are reused so leave address as is */ + ret = which; + } + + return ret; +} + +static void cpy_test_binding(struct test_binding *to, + struct test_binding *from, + struct test_function *old, + int func_count, + struct test_function *new) +{ + /* Reuse name */ + to->name = from->name; + to->target = get_new_binding_target(from->target, old, func_count, new); + + to->writable = 1; +} + +static struct test_binding *dup_test_bindings(struct test_binding *bindings, + struct test_function *old, + int func_count, + struct test_function *new) +{ + struct test_binding *b, *nb, *new_bindings; + int count = 0; + + for (b = bindings; b->name; ++b) + ++count; + + new_bindings = safe_calloc(count + 1, sizeof(*b)); + + for (b = bindings, nb = new_bindings; b->name; ++b, ++nb) + cpy_test_binding(nb, b, old, func_count, new); + + return new_bindings; +} + +static void cpy_test_config(struct test_config *to, + struct test_config *from) +{ + int func_count = 0; + struct test_function *f; + struct test_binding *b; + + /* Reuse label */ + to->label = from->label; + to->id = from->id; + to->strs = from->strs; + to->attrs = from->attrs; + + if (from->bound_funcs) { + /* If at least one function is not writable + we have to copy all of them */ + for (f = from->bound_funcs; f->instance; ++f) { + ++func_count; + if (!f->writable && !to->bound_funcs) { + to->bound_funcs = + dup_test_functions(from->bound_funcs); + } + } + + if (!f->name && !to->bound_funcs) + to->bound_funcs = from->bound_funcs; + } + + /* If bindings are set copy also them */ + if (from->bindings) { + /* If at least one function is not writable + we have to copy all of them */ + for (b = from->bindings; b->name; ++b) + if (!b->writable) + to->bindings = + dup_test_bindings(from->bindings, + from->bound_funcs, + func_count, + to->bound_funcs); + + /* if we are reusing binding we have to translate target + address to new one which is writable */ + if (!b->name && !to->bindings) { + to->bindings = from->bindings; + for (b = from->bindings; b->name; ++b) + b->target = + get_new_binding_target( + b->target, + from->bound_funcs, + func_count, + to->bound_funcs); + } + } + + to->writable = 1; +} + +static struct test_config *dup_test_configs(struct test_config *configs) +{ + struct test_config *c, *nc, *new_configs; + int count = 0; + + for (c = configs; c->label; ++c) + ++count; + + new_configs = safe_calloc(count + 1, sizeof(*c)); + + for (c = configs, nc = new_configs; c->label; ++c, ++nc) + cpy_test_config(nc, c); + + return new_configs; +} + +static void cpy_test_gadget(struct test_gadget *to, struct test_gadget *from) +{ + struct test_function *f; + struct test_config *c; + + /* Reuse name and udc */ + to->name = from->name; + to->udc = from->udc; + /* path is not being copied because it has not been allocated */ + + /* If at least one function is not writable + we have to copy all of them */ + for (f = from->functions; f->instance; ++f) + if (!f->writable) { + to->functions = + dup_test_functions(from->functions); + break; + } + + if (!f->name && !to->functions) + to->functions = from->functions; + + + /* If at least one config is not writable + we have to copy all of them */ + for (c = from->configs; c->label; ++c) + if (!c->writable) { + to->configs = dup_test_configs(from->configs); + break; + } + + if (!c->name && !to->configs) + to->configs = from->configs; + + to->writable = 1; +} + +static struct test_gadget *dup_test_gadgets(struct test_gadget *gadgets) +{ + struct test_gadget *g, *ng, *new_gadgets; + int count = 0; + + for (g = gadgets; g->name; ++g) + ++count; + + new_gadgets = safe_calloc(count + 1, sizeof(*g)); + + for (g = gadgets, ng = new_gadgets; g->name; ++g, ++ng) + cpy_test_gadget(ng, g); + + return new_gadgets; +} + +static struct test_state *dup_test_state(struct test_state *state) +{ + struct test_state *new_state; + struct test_gadget *g; + + new_state = safe_calloc(1, sizeof(*new_state)); + + /* We don't copy configfs path because it is never changed + if you would like to free it before test end replace + this code with strdup */ + new_state->configfs_path = state->configfs_path; + + /* path is not being copied because it has not been allocated */ + + /* If at least one gadget is not writable we have to copy all of them */ + for (g = state->gadgets; g->name; ++g) + if (!g->writable) { + new_state->gadgets = + dup_test_gadgets(state->gadgets); + break; + } + + if (!g->name && !new_state->gadgets) + new_state->gadgets = state->gadgets; + + /* udcs are also never changed so leave them as they are */ + new_state->udcs = state->udcs; + + new_state->writable = 1; + + return new_state; +} + +struct test_state *prepare_state(struct test_state *state) +{ + struct test_gadget *g; + struct test_state *new_state; + int count = 0; + + if (!state->writable) + new_state = dup_test_state(state); + else + new_state = state; + + safe_asprintf(&(new_state->path), "%s/usb_gadget", + new_state->configfs_path); + + for (g = new_state->gadgets; g->name; g++) { + prepare_gadget(new_state, g); + count++; + } + + qsort(new_state->gadgets, count, sizeof(*new_state->gadgets), + (int (*)(const void *, const void *))test_gadget_cmp); + + return new_state; +} + +struct test_state *build_empty_gadget_state(struct test_state *ts) +{ + struct test_state *ret; + struct test_gadget *tg; + int count = 0; + + ret = safe_malloc(sizeof(*ret)); + ret->udcs = ts->udcs; + ret->configfs_path = ts->configfs_path; + + for (tg = ts->gadgets; tg->name; ++tg) + count++; + + ret->gadgets = safe_calloc(count+1, sizeof(*ts->gadgets)); + memcpy(ret->gadgets, ts->gadgets, count*sizeof(*ts->gadgets)); + + for (tg = ret->gadgets; tg->name; ++tg) { + tg->configs = safe_calloc(1, sizeof(*tg->configs)); + tg->functions = safe_calloc(1, sizeof(*tg->functions)); + } + + return prepare_state(ret); +} + +/* Simulation of configfs for init */ + +static void push_binding(struct test_config *conf, struct test_binding *binding) +{ + char *s_path; + char *d_path; + + safe_asprintf(&s_path, "%s/%s/%s", conf->path, conf->name, binding->name); + safe_asprintf(&d_path, "%s/%s", binding->target->path, binding->target->name); + + PUSH_LINK(s_path, d_path, USBG_MAX_PATH_LENGTH - 1); +} + +static void push_config(struct test_config *c) +{ + struct test_binding *b; + int count = 0; + char *path; + + safe_asprintf(&path, "%s/%s", c->path, c->name); + + for (b = c->bindings; b->name; b++) + count++; + + PUSH_DIR(path, count); + for (b = c->bindings; b->name; b++) { + PUSH_DIR_ENTRY(b->name, DT_LNK); + push_binding(c, b); + } +} + +static void push_gadget(struct test_gadget *g) +{ + int count; + struct test_config *c; + struct test_function *f; + char *path; + + safe_asprintf(&path, "%s/%s/UDC", g->path, g->name); + PUSH_FILE(path, g->udc); + + count = 0; + for (f = g->functions; f->instance; f++) + count++; + + PUSH_DIR(f->path, count); + for (f = g->functions; f->instance; f++) + PUSH_DIR_ENTRY(f->name, DT_DIR); + + count = 0; + for (c = g->configs; c->label; c++) + count++; + + PUSH_DIR(c->path, count); + for (c = g->configs; c->label; c++) + PUSH_DIR_ENTRY(c->name, DT_DIR); + + for (c = g->configs; c->label; c++) + push_config(c); +} + +void push_init(struct test_state *state) +{ + char **udc; + struct test_gadget *g; + int count = 0; + + EXPECT_OPENDIR(state->path); + + for (udc = state->udcs; *udc; udc++) + count++; + + PUSH_DIR("/sys/class/udc", count); + for (udc = state->udcs; *udc; udc++) + PUSH_DIR_ENTRY(*udc, DT_REG); + + count = 0; + for (g = state->gadgets; g->name; g++) + count++; + + PUSH_DIR(state->path, count); + for (g = state->gadgets; g->name; g++) { + PUSH_DIR_ENTRY(g->name, DT_DIR); + } + + for (g = state->gadgets; g->name; g++) + push_gadget(g); +} + +int get_gadget_attr(usbg_gadget_attrs *attrs, usbg_gadget_attr attr) +{ + int ret = -1; + + switch (attr) { + case BCD_USB: + ret = attrs->bcdUSB; + break; + case B_DEVICE_CLASS: + ret = attrs->bDeviceClass; + break; + case B_DEVICE_SUB_CLASS: + ret = attrs->bDeviceSubClass; + break; + case B_DEVICE_PROTOCOL: + ret = attrs->bDeviceProtocol; + break; + case B_MAX_PACKET_SIZE_0: + ret = attrs->bMaxPacketSize0; + break; + case ID_VENDOR: + ret = attrs->idVendor; + break; + case ID_PRODUCT: + ret = attrs->idProduct; + break; + case BCD_DEVICE: + ret = attrs->bcdDevice; + break; + default: + ret = -1; + break; + } + + return ret; +} + +void pull_gadget_attribute(struct test_gadget *gadget, + usbg_gadget_attr attr, int value) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/%s", + gadget->path, gadget->name, usbg_get_gadget_attr_str(attr)); + + safe_asprintf(&content, "0x%x\n", value); + + EXPECT_HEX_WRITE(path, content); +} + +void push_gadget_attribute(struct test_gadget *gadget, + usbg_gadget_attr attr, int value) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/%s", + gadget->path, gadget->name, usbg_get_gadget_attr_str(attr)); + safe_asprintf(&content, "0x%x\n", value); + + PUSH_FILE(path, content); +} + +void push_gadget_attrs(struct test_gadget *gadget, usbg_gadget_attrs *attrs) +{ + int i; + + for (i = USBG_GADGET_ATTR_MIN; i < USBG_GADGET_ATTR_MAX; i++) + push_gadget_attribute(gadget, i, get_gadget_attr(attrs, i)); +} + +void pull_gadget_attrs(struct test_gadget *gadget, usbg_gadget_attrs *attrs) +{ + int i; + + for (i = USBG_GADGET_ATTR_MIN; i < USBG_GADGET_ATTR_MAX; i++) + pull_gadget_attribute(gadget, i, get_gadget_attr(attrs, i)); +} + +void init_with_state(struct test_state *in, usbg_state **out) +{ + int usbg_ret; + + push_init(in); + usbg_ret = usbg_init(in->configfs_path, out); + assert_int_equal(usbg_ret, USBG_SUCCESS); +} + +void safe_init_with_state(void **state, struct test_state **ts, usbg_state **s) +{ + *ts = (struct test_state *)(*state); + *state = NULL; + + init_with_state(*ts, s); + *state = *s; +} + +static int get_config_attr(usbg_config_attrs *attrs, config_attr attr) +{ + int ret; + + switch (attr) { + case MAX_POWER: + ret = attrs->bMaxPower; + break; + case BM_ATTRIBUTES: + ret = attrs->bmAttributes; + break; + default: + ret = -1; + break; + } + + return ret; +} + +void push_config_attribute(struct test_config *config, config_attr attr, + int value) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/%s", config->path, config->name, config_attr_names[attr]); + + switch (config_attr_format[attr]) { + case FORMAT_HEX: + safe_asprintf(&content, "0x%x\n", value); + break; + case FORMAT_DEC: + safe_asprintf(&content, "%d\n", value); + break; + } + + PUSH_FILE(path, content); +} + + +void push_config_attrs(struct test_config *config, usbg_config_attrs *attrs) +{ + int i; + + for (i = 0; i < CONFIG_ATTR_MAX; ++i) + push_config_attribute(config, i, get_config_attr(attrs, i)); +} + +void pull_config_attribute(struct test_config *config, config_attr attr, + int value) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/%s", config->path, config->name, config_attr_names[attr]); + + switch (config_attr_format[attr]) { + case FORMAT_HEX: + safe_asprintf(&content, "0x%x\n", value); + break; + case FORMAT_DEC: + safe_asprintf(&content, "%d\n", value); + break; + } + + switch (config_attr_format[attr]) { + case FORMAT_HEX: + EXPECT_HEX_WRITE(path, content); + break; + case FORMAT_DEC: + EXPECT_WRITE(path, content); + break; + } +} + +void pull_config_attrs(struct test_config *config, usbg_config_attrs *attrs) +{ + int i; + + for (i = 0; i < CONFIG_ATTR_MAX; ++i) + pull_config_attribute(config, i, get_config_attr(attrs, i)); +} + +const char *get_gadget_str(usbg_gadget_strs *strs, gadget_str str) +{ + const char *ret = NULL; + + switch (str) { + case STR_SER: + ret = strs->str_ser; + break; + case STR_MNF: + ret = strs->str_mnf; + break; + case STR_PRD: + ret = strs->str_prd; + break; + default: + ret = NULL; + break; + } + + return ret; +} + +static void pull_gadget_str_dir(struct test_gadget *gadget, int lang) +{ + char *dir; + int tmp; + + safe_asprintf(&dir, "%s/%s/strings/0x%x", + gadget->path, gadget->name, lang); + + srand(time(NULL)); + tmp = rand() % 2; + + if (tmp) { + EXPECT_OPENDIR(dir); + } else { + EXPECT_OPENDIR_ERROR(dir, ENOENT); + EXPECT_MKDIR(dir); + } +} + +static void pull_gadget_str(struct test_gadget *gadget, const char *attr_name, + int lang, const char *content) +{ + char *path; + + safe_asprintf(&path, "%s/%s/strings/0x%x/%s", + gadget->path, gadget->name, lang, attr_name); + EXPECT_WRITE(path, content); +} + +void pull_gadget_string(struct test_gadget *gadget, int lang, + gadget_str str, const char *content) +{ + pull_gadget_str_dir(gadget, lang); + pull_gadget_str(gadget, gadget_str_names[str], lang, content); +} + +void pull_gadget_strs(struct test_gadget *gadget, int lang, usbg_gadget_strs *strs) +{ + int i; + + pull_gadget_str_dir(gadget, lang); + for (i = 0; i < GADGET_STR_MAX; i++) + pull_gadget_str(gadget, gadget_str_names[i], lang, get_gadget_str(strs, i)); +} + +static void push_gadget_str_dir(struct test_gadget *gadget, int lang) +{ + char *dir; + + safe_asprintf(&dir, "%s/%s/strings/0x%x", + gadget->path, gadget->name, lang); + + EXPECT_OPENDIR(dir); +} + +static void push_gadget_str(struct test_gadget *gadget, const char *attr_name, + int lang, const char *content) +{ + char *path; + + safe_asprintf(&path, "%s/%s/strings/0x%x/%s", + gadget->path, gadget->name, lang, attr_name); + PUSH_FILE(path, content); +} + +void push_gadget_strs(struct test_gadget *gadget, int lang, usbg_gadget_strs *strs) +{ + int i; + + push_gadget_str_dir(gadget, lang); + for (i = 0; i < GADGET_STR_MAX; i++) + push_gadget_str(gadget, gadget_str_names[i], lang, get_gadget_str(strs, i)); +} + +void pull_config_string(struct test_config *config, int lang, const char *str) +{ + char *path; + int tmp; + + + safe_asprintf(&path, "%s/%s/strings/0x%x", + config->path, config->name, lang); + + srand(time(NULL)); + tmp = rand() % 2; + + if (tmp) { + EXPECT_OPENDIR(path); + } else { + EXPECT_OPENDIR_ERROR(path, ENOENT); + EXPECT_MKDIR(path); + } + + safe_asprintf(&path, "%s/configuration", path); + + EXPECT_WRITE(path, str); +} + +void pull_config_strs(struct test_config *config, int lang, usbg_config_strs *strs) +{ + pull_config_string(config, lang, strs->configuration); +} + +void push_config_string(struct test_config *config, int lang, const char *str) +{ + char *path; + + safe_asprintf(&path, "%s/%s/strings/0x%x", + config->path, config->name, lang); + + EXPECT_OPENDIR(path); + + safe_asprintf(&path, "%s/configuration", path); + + PUSH_FILE(path, str); +} + +void push_config_strs(struct test_config *config, int lang, usbg_config_strs *strs) +{ + push_config_string(config, lang, strs->configuration); +} + +void assert_config_attrs_equal(usbg_config_attrs *actual, usbg_config_attrs *expected) +{ + assert_int_equal(actual->bmAttributes, expected->bmAttributes); + assert_int_equal(actual->bMaxPower, expected->bMaxPower); +} + +void pull_create_config(struct test_config *tc) +{ + char *path; + + safe_asprintf(&path, "%s/%s", tc->path, tc->name); + EXPECT_MKDIR(path); + + if (tc->attrs) + pull_config_attrs(tc, tc->attrs); + if (tc->strs) + pull_config_strs(tc, LANG_US_ENG, tc->strs); +} + +#define ETHER_ADDR_STR_LEN 19 + +static void push_serial_attrs(struct test_function *func, + usbg_f_serial_attrs *attrs) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/port_num", func->path, func->name); + safe_asprintf(&content, "%d\n", attrs->port_num); + PUSH_FILE(path, content); +} + +static void push_net_attrs(struct test_function *func, + usbg_f_net_attrs *attrs) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/dev_addr", func->path, func->name); + + content = safe_malloc(ETHER_ADDR_STR_LEN * sizeof(char)); + ether_ntoa_r(&attrs->dev_addr, content); + + PUSH_FILE(path, content); + + path = safe_malloc(USBG_MAX_PATH_LENGTH * sizeof(char)); + sprintf(path, "%s/%s/host_addr", + func->path, func->name); + + content = safe_malloc(ETHER_ADDR_STR_LEN * sizeof(char)); + ether_ntoa_r(&attrs->host_addr, content); + + PUSH_FILE(path, content); + + safe_asprintf(&path, "%s/%s/qmult", func->path, func->name); + safe_asprintf(&content, "%d\n", attrs->qmult); + PUSH_FILE(path, content); + + safe_asprintf(&path, "%s/%s/ifname", func->path, func->name); + safe_asprintf(&content, "%s\n", attrs->ifname); + PUSH_FILE(path, content); +} + +static void push_phonet_attrs(struct test_function *func, + usbg_f_phonet_attrs *attrs) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/ifname", func->path, func->name); + safe_asprintf(&content, "%s\n", attrs->ifname); + PUSH_FILE(path, content); +} + +void push_function_attrs(struct test_function *func, usbg_function_attrs *function_attrs) +{ + int attrs_type; + usbg_f_attrs *attrs = &function_attrs->attrs; + + attrs_type = usbg_lookup_function_attrs_type(func->type); + + switch (attrs_type) { + case USBG_F_ATTRS_SERIAL: + push_serial_attrs(func, &attrs->serial); + break; + case USBG_F_ATTRS_NET: + push_net_attrs(func, &attrs->net); + break; + case USBG_F_ATTRS_PHONET: + push_phonet_attrs(func, &attrs->phonet); + break; + case USBG_F_ATTRS_FFS: + // ffs does not exist in filesystem + default: + break; + } +} + +static void pull_function_net_attrs(struct test_function *func, usbg_f_net_attrs *attrs) +{ + char *path; + char *content; + + safe_asprintf(&path, "%s/%s/dev_addr", func->path, func->name); + + content = safe_malloc(ETHER_ADDR_STR_LEN * sizeof(char)); + usbg_ether_ntoa_r(&attrs->dev_addr, content); + + EXPECT_WRITE(path, content); + + safe_asprintf(&path, "%s/%s/host_addr", func->path, func->name); + + content = safe_malloc(ETHER_ADDR_STR_LEN * sizeof(char)); + usbg_ether_ntoa_r(&attrs->host_addr, content); + + EXPECT_WRITE(path, content); + + safe_asprintf(&path, "%s/%s/qmult", func->path, func->name); + safe_asprintf(&content, "%d\n", attrs->qmult); + EXPECT_WRITE(path, content); +} + +void pull_function_attrs(struct test_function *func, usbg_function_attrs *attrs) +{ + /* only net attributes are writtable */ + if (attrs->header.attrs_type == USBG_F_ATTRS_NET) + pull_function_net_attrs(func, &attrs->attrs.net); +} + +void pull_create_function(struct test_function *tf) +{ + char *path; + int tmp; + + tmp = asprintf(&path, "%s/%s", tf->path, tf->name); + if (tmp < 0) + fail(); + free_later(path); + + EXPECT_MKDIR(path); + if (tf->attrs) + pull_function_attrs(tf, tf->attrs); +} + +void assert_func_equal(usbg_function *f, struct test_function *expected) +{ + assert_string_equal(f->instance, expected->instance); + assert_int_equal(f->type, expected->type); + assert_path_equal(f->path, expected->path); +} + +void assert_binding_equal(usbg_binding *b, struct test_binding *expected) +{ + assert_string_equal(b->name, expected->name); + assert_func_equal(b->target, expected->target); +} + +void assert_config_equal(usbg_config *c, struct test_config *expected) +{ + int i = 0; + usbg_binding *b; + + assert_int_equal(c->id, expected->id); + assert_string_equal(c->label, expected->label); + assert_path_equal(c->path, expected->path); + usbg_for_each_binding(b, c) + assert_binding_equal(b, &expected->bindings[i++]); +} + +void assert_gadget_equal(usbg_gadget *g, struct test_gadget *expected) +{ + usbg_config *c; + usbg_function *f; + int i; + + assert_string_equal(g->name, expected->name); + assert_path_equal(g->path, expected->path); + + i = 0; + usbg_for_each_function(f, g) + assert_func_equal(f, &expected->functions[i++]); + + i = 0; + usbg_for_each_config(c, g) + assert_config_equal(c, &expected->configs[i++]); +} + +void assert_state_equal(usbg_state *s, struct test_state *expected) +{ + usbg_gadget *g; + int i = 0; + + assert_path_equal(s->path, expected->path); + assert_path_equal(s->configfs_path, expected->configfs_path); + + usbg_for_each_gadget(g, s) + assert_gadget_equal(g, &expected->gadgets[i++]); +} + +#define SIGNUM(x) (((x) > 0) - ((x) < 0)) + +int path_cmp(const char *actual, const char *expected) +{ + const char *a = actual; + const char *b = expected; + + while (*a != '\0' && *b != '\0') { + if (*a != *b) + break; + do + ++a; + while (*a == '/'); + + do + ++b; + while (*b == '/'); + } + + return SIGNUM(*a - *b); +} + +int path_equal_display_error(const LargestIntegralType actual, const LargestIntegralType expected) +{ + if (path_cmp((const char *)actual, (const char *)expected) == 0) { + return 1; + } + + fprintf(stderr, "%s != %s\n", (const char *)actual, (const char *)expected); + return 0; +} + +void assert_path_equal(const char *actual, const char *expected) +{ + if (path_equal_display_error( + cast_to_largest_integral_type(actual), + cast_to_largest_integral_type(expected)) == 0) + fail(); +} + +int hex_str_cmp(const char *actual, const char *expected) +{ + int a, b; + + sscanf(actual, "%x", &a); + sscanf(expected, "%x", &b); + + return SIGNUM(a - b); +} + +int hex_str_equal_display_error(const LargestIntegralType actual, const LargestIntegralType expected) +{ + if (hex_str_cmp((const char *)actual, (const char *)expected) == 0) { + return 1; + } + + fprintf(stderr, "%s != %s\n", (const char *)actual, (const char *)expected); + return 0; +} + +void assert_gadget_attrs_equal(usbg_gadget_attrs *actual, + usbg_gadget_attrs *expected) +{ + int i; + + for (i = USBG_GADGET_ATTR_MIN; i < USBG_GADGET_ATTR_MAX; i++) + assert_int_equal(get_gadget_attr(actual, i), get_gadget_attr(expected, i)); +} + +void assert_gadget_strs_equal(usbg_gadget_strs *actual, usbg_gadget_strs *expected) +{ + int i; + for (i = 0; i < GADGET_STR_MAX; i++) + assert_string_equal(get_gadget_str(actual, i), get_gadget_str(expected, i)); +} + +void assert_f_serial_attrs_equal(usbg_f_serial_attrs *actual, + usbg_f_serial_attrs *expected) +{ + assert_int_equal(actual->port_num, expected->port_num); +} + +static void assert_ether_addrs_equal(const struct ether_addr *ea1, + const struct ether_addr *ea2) +{ + assert_memory_equal(ea1->ether_addr_octet, ea2->ether_addr_octet, + ETHER_ADDR_LEN); +} + +void assert_f_net_attrs_equal(usbg_f_net_attrs *actual, usbg_f_net_attrs *expected) +{ + assert_ether_addrs_equal(&actual->dev_addr, &expected->dev_addr); + assert_ether_addrs_equal(&actual->host_addr, &expected->host_addr); + assert_string_equal(actual->ifname, expected->ifname); + assert_int_equal(actual->qmult, expected->qmult); +} + +void assert_f_phonet_attrs_equal(usbg_f_phonet_attrs *actual, + usbg_f_phonet_attrs *expected) +{ + assert_string_equal(actual->ifname, expected->ifname); +} + +void assert_f_ffs_attrs_equal(usbg_f_ffs_attrs *actual, usbg_f_ffs_attrs *expected) +{ + assert_string_equal(actual->dev_name, expected->dev_name); +} + +void assert_function_attrs_equal(usbg_function_attrs *actual, + usbg_function_attrs *expected, usbg_f_attrs_type type) +{ + switch (type) { + case USBG_F_ATTRS_SERIAL: + assert_f_serial_attrs_equal(&actual->attrs.serial, &expected->attrs.serial); + break; + case USBG_F_ATTRS_NET: + assert_f_net_attrs_equal(&actual->attrs.net, &expected->attrs.net); + break; + case USBG_F_ATTRS_PHONET: + assert_f_phonet_attrs_equal(&actual->attrs.phonet, &expected->attrs.phonet); + break; + case USBG_F_ATTRS_FFS: + assert_f_ffs_attrs_equal(&actual->attrs.ffs, &expected->attrs.ffs); + break; + default: + fail(); + } +} + + +void for_each_test_function(struct test_state *ts, usbg_state *s, FunctionTest fun) +{ + struct test_gadget *tg; + struct test_function *tf; + usbg_gadget *g = NULL; + usbg_function *f = NULL; + + for (tg = ts->gadgets; tg->name; ++tg) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tf = tg->functions; tf->instance; ++tf) { + f = usbg_get_function(g, tf->type, tf->instance); + fun(f, tf); + } + } +} + +void for_each_test_config(struct test_state *ts, usbg_state *s, ConfigTest fun) +{ + usbg_gadget *g = NULL; + usbg_config *c = NULL; + struct test_gadget *tg; + struct test_config *tc; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tc = tg->configs; tc->label; tc++) { + c = usbg_get_config(g, tc->id, tc->label); + fun(c, tc); + } + } +} + +void for_each_binding(struct test_state *ts, usbg_state *s, BindingTestFunc fun) +{ + struct test_gadget *tg; + struct test_config *tc; + struct test_binding *tb; + usbg_gadget *g = NULL; + usbg_config *c = NULL; + usbg_binding *b = NULL; + + for (tg = ts->gadgets; tg->name; tg++) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + for (tc = tg->configs; tc->label; tc++) { + c = usbg_get_config(g, tc->id, tc->label); + assert_non_null(c); + + b = usbg_get_first_binding(c); + for (tb = tc->bindings; tb->name; ++tb) { + assert_non_null(b); + fun(tb, b); + b = usbg_get_next_binding(b); + } + } + } +} + +void for_each_test_gadget(struct test_state *ts, usbg_state *s, GadgetTestFunc fun) +{ + struct test_gadget *tg; + usbg_gadget *g = NULL; + + for (tg = ts->gadgets; tg->name; ++tg) { + g = usbg_get_gadget(s, tg->name); + assert_non_null(g); + fun(g, tg); + } +} diff --git a/tests/usbg-test.h b/tests/usbg-test.h new file mode 100644 index 0000000..127b90e --- /dev/null +++ b/tests/usbg-test.h @@ -0,0 +1,549 @@ +#ifndef USBG_TEST_H +#define USBG_TEST_H + +#include <usbg/usbg.h> +#include <sys/queue.h> +#include "usbg/usbg_internal.h" + +/* Simple structures for defining gadgets. All arrays should be null-terminated.*/ + +/** + * @file tests/usbg-test.h + */ + +struct test_function +{ + usbg_function_type type; + char *instance; + + char *path; + char *name; + usbg_function_attrs *attrs; + int writable; +}; + +struct test_binding +{ + struct test_function *target; + char *name; + int writable; +}; + +struct test_config +{ + char *label; + int id; + struct test_function *bound_funcs; + + struct test_binding *bindings; + char *path; + char *name; + int writable; + usbg_config_strs *strs; + usbg_config_attrs *attrs; +}; + +struct test_gadget +{ + char *name; + char *udc; + struct test_config *configs; + struct test_function *functions; + + char *path; + int writable; +}; + +struct test_state +{ + char *configfs_path; + /* filled by prepare_state() */ + char *path; + struct test_gadget *gadgets; + char **udcs; + int writable; +}; + +typedef enum { + STR_SER = 0, + STR_MNF, + STR_PRD, + GADGET_STR_MAX +} gadget_str; + +typedef enum { + MAX_POWER = 0, + BM_ATTRIBUTES, + CONFIG_ATTR_MAX +} config_attr; + +typedef enum { + FORMAT_HEX, + FORMAT_DEC +} attr_format; + +#define TEST_FUNCTION_LIST_END { \ + .instance = NULL, \ + } + +#define TEST_CONFIG_LIST_END { \ + .label = NULL, \ + .bindings = NULL, \ + } + +#define TEST_GADGET_LIST_END { \ + .name = NULL, \ + .udc = NULL, \ + .configs = NULL, \ + .functions = NULL, \ + } + +#define expect_path(function, param, data) \ + expect_check(function, param, \ + (CheckParameterValue)(path_equal_display_error), data) + +/** + * @brief Prepare given state for using in tests + * @details Generate full pathes to state elements and sort state's content. + * Must be called before pasing state to push_* and pull_* functions. + * @param[in] state State schema used to genrate test state + * @return Pointer to state which can be used for testing. Returned value is + * equal to #state if writable attribute has been set to 1 or pointer + * to newly allocated test_state filled with suitable values. All memory + * allocated in this function is scheduled to free using free_later(). + */ +struct test_state *prepare_state(struct test_state *state); + +/** + * @brief Prepare given config for using in tests + * @details Generate required pathes for given config and sort content + * (i.e. binding list) + * @param[in] c Config to be filled out + * @param[in] cpath Path to configs directory + * @param[in] fpath Path to functions directory + */ +void prepare_config(struct test_config *c, char *cpath, char *fpath); + +/** + * @brief Prepare given function for using in tests + * @details Generate required pathes for given function + * @param[in] f Function to be filled out + * @param[in] path Path to functions directory + */ +void prepare_function(struct test_function *f, char *path); + +/** + * @brief Prepare given gadget for using in tests + * @details Generate required paths for given gadget and sort it's content + * (i.e. functions list and config list) + * @param[in] state Pointer to gadget's parent state + * @param[in] g Gadget to be filled out + */ +void prepare_gadget(struct test_state *state, struct test_gadget *g); + +/** + * @brief Fill given binding with required values + * @details Make given binding point to a function + * @param[in] b Test binding to be prepared + * @param[in] f Function to which binding will point + * @param[in] fpath Path to functions directory + */ +void prepare_binding(struct test_binding *b, struct test_function *f, char *fpath); + +/** + * @brief Prepare fake filesystem to init usbg with given test state + * @details Use wrapped i/o functions to simulate configfs state for usbg. + * Calling usbg_init without preparation and with mocked i/o functions + * may fail. + * @param[in] state Fake state of configfs defined in test + */ +void push_init(struct test_state *state); + +/** + * Prepare specific attributes writting/reading + **/ + +/** + * @brief Prepare for getting config attributes + * @param[in] config Configuration from which attributes will be get + * @param[in] attrs Attributes which will be present in virtual filesystem + */ +void push_config_attrs(struct test_config *config, usbg_config_attrs *attrs); + +/** + * @brief Preapre for setting config attributes + * @param[in] config Configuration on which attributes will be set + * @param[in] attrs Attributes which will be set on given config + */ +void pull_config_attrs(struct test_config *config, usbg_config_attrs *attrs); + +/** + * @brief Get gadget attribute + * @param[in] attrs + * @param[in] attr + */ +int get_gadget_attr(usbg_gadget_attrs *attrs, usbg_gadget_attr attr); + +/** + * @brief Prepare to write given attribute by libusbg + * @param[in] gadget Test gadget related to given attribute + * @param[in] attr Attribute + * @param[in] value Attributes value + **/ +void push_gadget_attribute(struct test_gadget *gadget, + usbg_gadget_attr attr, int value); + +/** + * @brief Prepare to read given attribute by libusbg + * @param[in] gadget Test gadget related to given attribute + * @param[in] attr Attribute + * @param[in] value Attributes value + **/ +void pull_gadget_attribute(struct test_gadget *gadget, + usbg_gadget_attr attr, int value); + +/** + * @brief Prepare fake filesystem to get given gadget attributes + * @details Prepare queue of values passed to wrapped i/o functions, + * all values got from given attributes structure. + * @param[in] gadget Pointer to gadget + * @param[in] attrs Pointer to attributes which gadget should have + * @warning Calling usbg_get_gadget_attrs function whithout this + * preparation and with wrapped i/o may fail. + */ +void push_gadget_attrs(struct test_gadget *gadget, usbg_gadget_attrs *attrs); + +/** + * @brief Prepare fake filesystem for attributes setting attempt. + * @details Prepare queue of values passed to wrapped i/o functions, + * corresponding to functions called on attributes setting + * @param[in] gadget Pointer to gadget + * @param[in] attrs Pointer to expected attributes + * @warning Calling usbg_get_gadget_attrs function whithout this + * preparation and with wrapped i/o may fail. + */ +void pull_gadget_attrs(struct test_gadget *gadget, usbg_gadget_attrs *attrs); + +/** + * @brief Prepare fake filesystem to get given function attributes + * @details Prepare queue of values passed to wrapped i/o functions, + * all values got from given attributes structure. + * @warning Calling usbg_get_function_attrs function whithout this + * preparation and with wrapped i/o may fail. + */ +void push_function_attrs(struct test_function *func, usbg_function_attrs *attrs); + +/** + * @brief Prepare fake filesystem to set given function attributes + * @details Prepare queue of values passed to wrapped i/o functions, + * all values got from given attributes structure. + * @warning Calling usbg_set_function_attrs function whithout this + * preparation and with wrapped i/o may fail. + */ +void pull_function_attrs(struct test_function *func, usbg_function_attrs *attrs); + +/** + * @brief Get gadget string + * @param[in] strs Set of gadget strings + * @param[in] str Identifier of string which should be returned + * @return Selected string from given set of strings + */ +const char *get_gadget_str(usbg_gadget_strs *strs, gadget_str str); + +/** + * @brief Prepare filesystem to set selected gadget string + * @param[in] gadget Gadget on which str will be set + * @param[in] lang Language of string + * @param[in] str String identifier + * @param[in] content String expected to be set + */ +void pull_gadget_string(struct test_gadget *gadget, int lang, + gadget_str str, const char *content); + +/** + * @brief Prepare filesystem to set given gadget strings + * @param[in] gadget Gadget on which strings will be set + * @param[in] lang Language of strings + * @param[in] strs Strings expected to be set + */ +void pull_gadget_strs(struct test_gadget *gadget, int lang, usbg_gadget_strs *strs); + +/** + * @brief prepare for reading gadget's strings + */ +void push_gadget_strs(struct test_gadget *gadget, int lang, usbg_gadget_strs *strs); + +/** + * @brief Prepare for /ref usbg_set_config_string calling + * @details Expect setting the same string as given one + * @param[in] config on which strings will be set + * @param[in] lang Language of strings + * @param[in] str string to be set as configuration string + */ +void pull_config_string(struct test_config *config, int lang, const char *str); + +/** + * @brief Prepare for writting given config strings + * @param[in] config on which strings will be set + * @param[in] lang Language of strings + * @param[in] strs Strings expected to be set + */ +void pull_config_strs(struct test_config *config, int lang, usbg_config_strs *strs); + +/** + * @brief Prepare for /ref usbg_get_config_string calling + * @details Expect setting the same string as given one + * @param[in] config from which strings will be get + * @param[in] lang Language of strings + * @param[in] str string which should be returned as configuration string + */ +void push_config_string(struct test_config *config, int lang, const char *str); + +/** + * @brief Prepare for reading config strings + * @param[in] config from which strings will be get + * @param[in] lang Language of strings + * @param[in] strs Strings which should be returned + */ +void push_config_strs(struct test_config *config, int lang, usbg_config_strs *strs); + +/** + * @brief Prepare for creating config + * @param[in] tc Test config to be created + */ +void pull_create_config(struct test_config *tc); + +/** + * @brief Prepare for creating function + * @param[in] tf Test function to be created + */ +void pull_create_function(struct test_function *tf); + +/** + * @brief Copy state without configs and functions + * @param[in] ts State to bo copied + * @return State with empty gadgets + */ +struct test_state *build_empty_gadget_state(struct test_state *ts); + +/** + * @brief Store given pointer on cleanup stack + * @details All stacked pointers will be freed by calling cleanup_queue. + * This can be used to manage memory needed for single test casees. + */ +void free_later(void *ptr); + +/** + * @brief Cleans up memory no longer needed + * @details Frees all pointer stored on cleanup stack by calling free_later + * @warning Calling this function before end of single test usually make test state + * unusable. Use it only when you no longer need allocated data (at the end of + * test case, in most cases) + */ +void cleanup_stack(); + +/** + * @brief init usbg with given test state + */ +void init_with_state(struct test_state *in, usbg_state **out); + +/** + * @brief Safely initialize usbg state from pointer given to test + * @param[in] state Pointer given to test function + * @param[out] ts Pointer to be filled with test state + * @param[out] s Pointer to be filled with usbg state + */ +void safe_init_with_state(void **state, struct test_state **ts, usbg_state **s); + +/** + * @brief Assert that given config attributes are equal + */ +void assert_config_attrs_equal(usbg_config_attrs *actual, usbg_config_attrs *expected); + +/** + * @brief Assert that given usbg binding matches given test binding + * @param[in] f Pointer to usbg binding struct + * @param[in] expected Pointer to test binding struct with expected values + */ +void assert_binding_equal(usbg_binding *b, struct test_binding *expected); + +/** + * @brief Assert that given usbg function matches given test function + * @param[in] f Pointer to usbg function struct + * @param[in] expected Pointer to test function struct with expected values + */ +void assert_func_equal(usbg_function *f, struct test_function *expected); + +/** + * @brief Assert that given usbg config matches given test config + * @param[in] c Pointer to usbg config struct + * @param[in] expected Pointer to test config struct with expected values + */ +void assert_config_equal(usbg_config *c, struct test_config *expected); + +/** + * @brief Assert that given usbg gadget matches given test gadget + * @param[in] g Pointer to usbg gadget struct + * @param[in] expected Pointer to test gadget struct with expected values + */ +void assert_gadget_equal(usbg_gadget *g, struct test_gadget *expected); + +/** + * @brief Assert that given usbg state matches given test state + * @param[in] s Pointer to usbg state struct + * @param[in] expected Pointer to test state struct with expected values + */ +void assert_state_equal(usbg_state *s, struct test_state *expected); + +/** + * @brief Compare path names + * @details Given pathes don't need to exist + * @return Integer less than, equal to, or greater than zero if a is (respectively) + * less than, equal to, or greater than b. + */ +int path_cmp(const char *a, const char *b); + +/** + * @brief Print error when given paths are not equal + * @return 1 if paths are equal, 0 otherwise + * @note Argument type is defined by cmocka. This specific function type is defined + * as custom comparing function in cmocka framework. + */ +int path_equal_display_error(const LargestIntegralType actual, const LargestIntegralType expected); + +/** + * @brief Compare attributes (as strings) + * @return Integer less than, equal to, or greater than zero if a is (respectively) + * less than, equal to, or greater than b. + */ +int hex_str_cmp(const char *actual, const char *expected); + +/** + * @brief Print error when given attributes are not equal + * @return 1 if attributes are equal, 0 otherwise + * @note Argument type is defined by cmocka. This specific function type is defined + * as custom comparing function in cmocka framework. + */ +int hex_str_equal_display_error(const LargestIntegralType actual, const LargestIntegralType expected); + +/** + * @brief Assert that given path strings are equal + * @details Given pathes don't need to exist + */ +void assert_path_equal(const char *actual, const char *expected); + +/** + * @brief Assert that given usbg gadget attributes sets are equal + * @param[in] actual Pointer to actual gadget attributes structure + * @param[in] expected Pointer to expeced gadget attributes structure + */ +void assert_gadget_attrs_equal(usbg_gadget_attrs *actual, + usbg_gadget_attrs *expected); + +/** + * @brief Assert that given function attributes are the same. + * @param[in] actual Pointer to actual attributes object + * @param[in] expected Pointer to expected attributes obejct + * @param[in] type Type of function, which attributes are checked + */ +void assert_function_attrs_equal(usbg_function_attrs *actual, + usbg_function_attrs *expected, usbg_f_attrs_type type); + +/** + * @brief Assert that given gadget strings are equal + */ +void assert_gadget_strs_equal(usbg_gadget_strs *actual, usbg_gadget_strs *expected); + +/** + * @brief Function that performs some test on given usbg function +*/ +typedef void (*FunctionTest)(usbg_function *f, struct test_function *tf); + +/** + * @brief Call given function for all usb functions present in given state + * @param[in] ts Test state to be tested + * @param[in] s Properly prepared usbg state to be tested + * @param[in] fun Function to be called on each usb function in state + */ +void for_each_test_function(struct test_state *ts, usbg_state *s, FunctionTest fun); + +/** + * @brief Function that performs some test on given usbg config +*/ +typedef void (*ConfigTest)(usbg_config *c, struct test_config *tc); + +/** + * @brief Call given function for all usb configs present in given state + * @param[in] ts Test state to be tested + * @param[in] s Properly prepared usbg state to be tested + * @param[in] fun Function to be called on each usb function in state + */ +void for_each_test_config(struct test_state *ts, usbg_state *s, ConfigTest fun); + +/** + * @brief Function that performs test on given usbg binding + */ +typedef void (*BindingTestFunc)(struct test_binding *tb, usbg_binding *b); + +/** + * @brief Call given function for all usb bindings present in given state + * @param[in] ts Test state to be tested + * @param[in] s Properly prepared usbg state to be tested + * @param[in] fun Function to be called on each usb binding in state + */ +void for_each_binding(struct test_state *ts, usbg_state *s, BindingTestFunc fun); + +/** + * @brief Function that performs test on given usbg gadget + */ +typedef void (*GadgetTestFunc)(usbg_gadget *g, struct test_gadget *tg); + +/** + * @brief Call given function for all usb gadgets present in given state + * @param[in] ts Test state to be tested + * @param[in] s Properly prepared usbg state to be tested + * @param[in] fun Function to be called on each usb gadget in state + */ +void for_each_test_gadget(struct test_state *ts, usbg_state *s, GadgetTestFunc fun); + +static inline void *safe_calloc(int count, size_t size) +{ + void *ptr; + + ptr = calloc(count, size); + if (ptr == NULL) + fail(); + + free_later(ptr); + return ptr; +} + +static inline void *safe_malloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) + fail(); + + free_later(ptr); + return ptr; +} + +static inline int safe_asprintf(char **ptr, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = vasprintf(ptr, fmt, args); + va_end(args); + + if (ret < 0) + fail(); + + free_later(*ptr); + return ret; +} + +#endif /* USBG_TEST_H */ |