/* SPDX-License-Identifier: GPL-2.0-only */ /* Include memcpy() source code and alter its name to compare results with libc memcpy() */ #define memcpy cb_memcpy #include "../lib/memcpy.c" #undef memcpy #include #include #include #include #define MEMCPY_BUFFER_SZ (4 * KiB) /* Prototype of memcpy() from string.h was changed to cb_memcpy(). It has to be defined again. */ void *memcpy(void *dest, const void *src, size_t n); struct test_memcpy_data { u8 *buffer_from; u8 *buffer_to; u8 *helper_buffer; }; int setup_test(void **state) { struct test_memcpy_data *s = malloc(sizeof(struct test_memcpy_data)); if (!s) return -1; s->buffer_from = malloc(MEMCPY_BUFFER_SZ); s->buffer_to = malloc(MEMCPY_BUFFER_SZ); s->helper_buffer = malloc(MEMCPY_BUFFER_SZ); if (!s->buffer_from || !s->buffer_to || !s->helper_buffer) { free(s->buffer_from); free(s->buffer_to); free(s->helper_buffer); free(s); return -1; } /* Fill buffers with different values (other than zero) to make them distinguishable. The helper buffer is often used as a backup of destination buffer so it has the same value. */ memset(s->buffer_from, 0xAB, MEMCPY_BUFFER_SZ); memset(s->buffer_to, 0xBC, MEMCPY_BUFFER_SZ); memset(s->helper_buffer, 0xBC, MEMCPY_BUFFER_SZ); *state = s; return 0; } int teardown_test(void **state) { struct test_memcpy_data *s = *state; free(s->buffer_from); free(s->buffer_to); free(s->helper_buffer); free(s); return 0; } /* Fill buffer with values from provided range [start; end] in circular way. */ static void fill_buffer_data_range(u8 *buffer, size_t sz, u8 start_value, u8 end_value) { for (size_t i = 0; i < sz; ++i) buffer[i] = (start_value + i) % (end_value - start_value + 1); } static void test_memcpy_full_buffer_copy(void **state) { struct test_memcpy_data *s = *state; void *res_cb; fill_buffer_data_range(s->buffer_from, MEMCPY_BUFFER_SZ, 0, 255); res_cb = cb_memcpy(s->buffer_to, s->buffer_from, MEMCPY_BUFFER_SZ); assert_ptr_equal(s->buffer_to, res_cb); assert_memory_equal(s->buffer_from, s->buffer_to, MEMCPY_BUFFER_SZ); } static void test_memcpy_zero_size(void **state) { struct test_memcpy_data *s = *state; void *res_cb; fill_buffer_data_range(s->buffer_from, MEMCPY_BUFFER_SZ, 'A', 'Z'); /* Expect no change in destination buffer. */ res_cb = cb_memcpy(s->buffer_to, s->buffer_from, 0); assert_ptr_equal(s->buffer_to, res_cb); assert_memory_equal(s->buffer_to, s->helper_buffer, MEMCPY_BUFFER_SZ); } static void test_memcpy_buffer_part(void **state) { struct test_memcpy_data *s = *state; void *res_cb; const size_t offset = MEMCPY_BUFFER_SZ / 4; const size_t sz = MEMCPY_BUFFER_SZ / 2; /* Self-test for correct data ranges */ assert_true(offset + sz <= MEMCPY_BUFFER_SZ); fill_buffer_data_range(s->buffer_from, MEMCPY_BUFFER_SZ, '0', '9'); /* Expect only *sz* bytes of buffer to be copied. Helper buffer is used as template. */ res_cb = cb_memcpy(s->buffer_to + offset, s->buffer_from, sz); assert_ptr_equal(s->buffer_to + offset, res_cb); assert_memory_equal(s->buffer_to, s->helper_buffer, offset); assert_memory_equal(s->buffer_to + offset, s->buffer_from, sz); assert_memory_equal(s->buffer_to + offset + sz, s->helper_buffer + offset + sz, MEMCPY_BUFFER_SZ - (offset + sz)); } static void test_memcpy_buffer_part_unaligned(void **state) { struct test_memcpy_data *s = *state; void *res_cb; const size_t dst_offset = MEMCPY_BUFFER_SZ / 8 + 3; const size_t src_offset = MEMCPY_BUFFER_SZ / 4 - 3; const size_t sz = MEMCPY_BUFFER_SZ / 4 + 7; /* Self-test for correct data ranges */ assert_true(dst_offset + sz <= MEMCPY_BUFFER_SZ); assert_true(src_offset + sz <= MEMCPY_BUFFER_SZ); fill_buffer_data_range(s->buffer_from, MEMCPY_BUFFER_SZ, 0x13, 0xB7); res_cb = cb_memcpy(s->buffer_to + dst_offset, s->buffer_from + src_offset, sz); assert_ptr_equal(s->buffer_to + dst_offset, res_cb); assert_memory_equal(s->buffer_to, s->helper_buffer, dst_offset); assert_memory_equal(s->buffer_to + dst_offset, s->buffer_from + src_offset, sz); assert_memory_equal(s->buffer_to + dst_offset + sz, s->helper_buffer + dst_offset + sz, MEMCPY_BUFFER_SZ - (dst_offset + sz)); } static void test_memcpy_copy_to_itself(void **state) { struct test_memcpy_data *s = *state; void *res_cb; fill_buffer_data_range(s->buffer_to, MEMCPY_BUFFER_SZ, 'G', 'X'); memcpy(s->buffer_to, s->helper_buffer, MEMCPY_BUFFER_SZ); /* Expect no change in source/destination buffer. */ res_cb = cb_memcpy(s->buffer_to, s->buffer_to, MEMCPY_BUFFER_SZ); assert_ptr_equal(s->buffer_to, res_cb); assert_memory_equal(s->buffer_to, s->helper_buffer, MEMCPY_BUFFER_SZ); } static void test_memcpy_copy_part_of_itself_to_itself(void **state) { struct test_memcpy_data *s = *state; void *res_cb; const size_t offset = MEMCPY_BUFFER_SZ / 8; const size_t sz = MEMCPY_BUFFER_SZ - offset; /* Self-test for correct data ranges */ assert_true(offset + sz <= MEMCPY_BUFFER_SZ); fill_buffer_data_range(s->buffer_to, MEMCPY_BUFFER_SZ, 'd', 'v'); memcpy(s->helper_buffer, s->buffer_to, MEMCPY_BUFFER_SZ); /* Expect only *sz* bytes to be overwritten and *offset* bytes to be left at the end. */ res_cb = cb_memcpy(s->buffer_to, s->buffer_to + offset, sz); assert_ptr_equal(s->buffer_to, res_cb); assert_memory_equal(s->buffer_to, s->helper_buffer + offset, sz); assert_memory_equal(s->buffer_to + sz, s->helper_buffer + sz, offset); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(test_memcpy_full_buffer_copy, setup_test, teardown_test), cmocka_unit_test_setup_teardown(test_memcpy_zero_size, setup_test, teardown_test), cmocka_unit_test_setup_teardown(test_memcpy_buffer_part, setup_test, teardown_test), cmocka_unit_test_setup_teardown(test_memcpy_buffer_part_unaligned, setup_test, teardown_test), cmocka_unit_test_setup_teardown(test_memcpy_copy_to_itself, setup_test, teardown_test), cmocka_unit_test_setup_teardown(test_memcpy_copy_part_of_itself_to_itself, setup_test, teardown_test), }; return cb_run_group_tests(tests, NULL, NULL); }