diff --git a/include/test/lib.h b/include/test/lib.h new file mode 100644 index 0000000000..04b6241e54 --- /dev/null +++ b/include/test/lib.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 Heinrich Schuchardt + */ + +#ifndef __TEST_LIB_H__ +#define __TEST_LIB_H__ + +#include + +/* Declare a new library function test */ +#define LIB_TEST(_name, _flags) UNIT_TEST(_name, _flags, lib_test) + +#endif /* __TEST_LIB_H__ */ diff --git a/include/test/suites.h b/include/test/suites.h index 77d863b4a6..01bee09346 100644 --- a/include/test/suites.h +++ b/include/test/suites.h @@ -27,6 +27,7 @@ int do_ut_bloblist(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); int do_ut_compression(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); int do_ut_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_env(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +int do_ut_lib(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_overlay(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_unicode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); diff --git a/test/Kconfig b/test/Kconfig index de16d179d0..48a0e501f8 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -6,6 +6,14 @@ menuconfig UNIT_TEST This does not require sandbox to be included, but it is most often used there. +config UT_LIB + bool "Unit tests for library functions" + depends on UNIT_TEST + default y + help + Enables the 'ut lib' command which tests library functions like + memcat(), memcyp(), memmove(). + config UT_TIME bool "Unit tests for time functions" depends on UNIT_TEST diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 56924a5272..e3b89504e7 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -46,6 +46,9 @@ static cmd_tbl_t cmd_ut_sub[] = { #ifdef CONFIG_UT_OVERLAY U_BOOT_CMD_MKENT(overlay, CONFIG_SYS_MAXARGS, 1, do_ut_overlay, "", ""), #endif +#ifdef CONFIG_UT_LIB + U_BOOT_CMD_MKENT(lib, CONFIG_SYS_MAXARGS, 1, do_ut_lib, "", ""), +#endif #ifdef CONFIG_UT_TIME U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""), #endif @@ -108,6 +111,9 @@ static char ut_help_text[] = #ifdef CONFIG_UT_ENV "ut env [test-name]\n" #endif +#ifdef CONFIG_UT_LIB + "ut lib [test-name] - test library functions\n" +#endif #ifdef CONFIG_UT_OVERLAY "ut overlay [test-name]\n" #endif diff --git a/test/lib/Makefile b/test/lib/Makefile index 5a636aac74..308c61708e 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -2,5 +2,7 @@ # # (C) Copyright 2018 # Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc +obj-y += cmd_ut_lib.o obj-y += hexdump.o obj-y += lmb.o +obj-y += string.o diff --git a/test/lib/cmd_ut_lib.c b/test/lib/cmd_ut_lib.c new file mode 100644 index 0000000000..eb90e53914 --- /dev/null +++ b/test/lib/cmd_ut_lib.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Heinrich Schuchardt + * + * Unit tests for library functions + */ + +#include +#include +#include +#include +#include + +int do_ut_lib(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct unit_test *tests = ll_entry_start(struct unit_test, lib_test); + const int n_ents = ll_entry_count(struct unit_test, lib_test); + + return cmd_ut_category("lib", tests, n_ents, argc, argv); +} diff --git a/test/lib/string.c b/test/lib/string.c new file mode 100644 index 0000000000..8e246ab4ed --- /dev/null +++ b/test/lib/string.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Heinrich Schuchardt + * + * Unit tests for memory functions + * + * The architecture dependent implementations run through different lines of + * code depending on the alignment and length of memory regions copied or set. + * This has to be considered in testing. + */ + +#include +#include +#include +#include +#include + +/* Xor mask used for marking memory regions */ +#define MASK 0xA5 +/* Number of different alignment values */ +#define SWEEP 16 +/* Allow for copying up to 32 bytes */ +#define BUFLEN (SWEEP + 33) + +/** + * init_buffer() - initialize buffer + * + * The buffer is filled with incrementing values xor'ed with the mask. + * + * @buf: buffer + * @mask: xor mask + */ +static void init_buffer(u8 buf[], u8 mask) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) + buf[i] = i ^ mask; +} + +/** + * test_memset() - test result of memset() + * + * @uts: unit test state + * @buf: buffer + * @mask: value set by memset() + * @offset: relative start of region changed by memset() in buffer + * @len: length of region changed by memset() + * Return: 0 = success, 1 = failure + */ +static int test_memset(struct unit_test_state *uts, u8 buf[], u8 mask, + int offset, int len) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) { + if (i < offset || i >= offset + len) { + ut_asserteq(i, buf[i]); + } else { + ut_asserteq(mask, buf[i]); + } + } + return 0; +} + +/** + * lib_memset() - unit test for memset() + * + * Test memset() with varied alignment and length of the changed buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memset(struct unit_test_state *uts) +{ + u8 buf[BUFLEN]; + int offset, len; + void *ptr; + + for (offset = 0; offset <= SWEEP; ++offset) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf, 0); + ptr = memset(buf + offset, MASK, len); + ut_asserteq_ptr(buf + offset, (u8 *)ptr); + if (test_memset(uts, buf, MASK, offset, len)) { + debug("%s: failure %d, %d\n", + __func__, offset, len); + return CMD_RET_FAILURE; + } + } + } + return 0; +} + +LIB_TEST(lib_memset, 0); + +/** + * test_memmove() - test result of memcpy() or memmove() + * + * @uts: unit test state + * @buf: buffer + * @mask: xor mask used to initialize source buffer + * @offset1: relative start of copied region in source buffer + * @offset2: relative start of copied region in destination buffer + * @len: length of region changed by memset() + * Return: 0 = success, 1 = failure + */ +static int test_memmove(struct unit_test_state *uts, u8 buf[], u8 mask, + int offset1, int offset2, int len) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) { + if (i < offset2 || i >= offset2 + len) { + ut_asserteq(i, buf[i]); + } else { + ut_asserteq((i + offset1 - offset2) ^ mask, buf[i]); + } + } + return 0; +} + +/** + * lib_memcpy() - unit test for memcpy() + * + * Test memcpy() with varied alignment and length of the copied buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memcpy(struct unit_test_state *uts) +{ + u8 buf1[BUFLEN]; + u8 buf2[BUFLEN]; + int offset1, offset2, len; + void *ptr; + + init_buffer(buf1, MASK); + + for (offset1 = 0; offset1 <= SWEEP; ++offset1) { + for (offset2 = 0; offset2 <= SWEEP; ++offset2) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf2, 0); + ptr = memcpy(buf2 + offset2, buf1 + offset1, + len); + ut_asserteq_ptr(buf2 + offset2, (u8 *)ptr); + if (test_memmove(uts, buf2, MASK, offset1, + offset2, len)) { + debug("%s: failure %d, %d, %d\n", + __func__, offset1, offset2, len); + return CMD_RET_FAILURE; + } + } + } + } + return 0; +} + +LIB_TEST(lib_memcpy, 0); + +/** + * lib_memmove() - unit test for memmove() + * + * Test memmove() with varied alignment and length of the copied buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memmove(struct unit_test_state *uts) +{ + u8 buf[BUFLEN]; + int offset1, offset2, len; + void *ptr; + + for (offset1 = 0; offset1 <= SWEEP; ++offset1) { + for (offset2 = 0; offset2 <= SWEEP; ++offset2) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf, 0); + ptr = memmove(buf + offset2, buf + offset1, + len); + ut_asserteq_ptr(buf + offset2, (u8 *)ptr); + if (test_memmove(uts, buf, 0, offset1, offset2, + len)) { + debug("%s: failure %d, %d, %d\n", + __func__, offset1, offset2, len); + return CMD_RET_FAILURE; + } + } + } + } + return 0; +} + +LIB_TEST(lib_memmove, 0);