Edward O'Callaghan has submitted this change. ( https://review.coreboot.org/c/flashrom/+/56413 )
Change subject: tests: Mock file i/o for linux_mtd and linux_spi tests ......................................................................
tests: Mock file i/o for linux_mtd and linux_spi tests
This patch adds an init-shutdown test for linux_mtd. Since linux_mtd is using file i/o operations, those are added to the framework and mocked.
Another driver linux_spi which is also using file i/o, got an upgrade in this patch, and it is now reading max buffer size from sysfs (using mocked file i/o).
A good side-effect is that linux_mtd is the first test for opaque masters, which is great to have in preparation for a change like CB:56103 but for opaque masters.
BUG=b:181803212 TEST=builds and ninja test
Change-Id: I73f0d6ff2ad5074add7a721ed3416230d3647e3f Signed-off-by: Anastasia Klimchuk aklm@chromium.org Reviewed-on: https://review.coreboot.org/c/flashrom/+/56413 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Nico Huber nico.h@gmx.de Reviewed-by: Edward O'Callaghan quasisec@chromium.org --- M tests/init_shutdown.c M tests/io_mock.h M tests/meson.build M tests/tests.c M tests/tests.h 5 files changed, 180 insertions(+), 4 deletions(-)
Approvals: build bot (Jenkins): Verified Nico Huber: Looks good to me, but someone else must approve Edward O'Callaghan: Looks good to me, approved
diff --git a/tests/init_shutdown.c b/tests/init_shutdown.c index d1d0c7f..3236b22 100644 --- a/tests/init_shutdown.c +++ b/tests/init_shutdown.c @@ -19,6 +19,8 @@ #include "io_mock.h" #include "programmer.h"
+#define NOT_NULL ((void *)0xf000baaa) + static void run_lifecycle(void **state, const struct programmer_entry *prog, const char *param) { (void) state; /* unused */ @@ -195,16 +197,107 @@ #endif }
+struct linux_mtd_io_state { + char *fopen_path; +}; + +FILE *linux_mtd_fopen(void *state, const char *pathname, const char *mode) +{ + struct linux_mtd_io_state *io_state = state; + + io_state->fopen_path = strdup(pathname); + + return NOT_NULL; +} + +size_t linux_mtd_fread(void *state, void *buf, size_t size, size_t len, FILE *fp) +{ + struct linux_mtd_fread_mock_entry { + const char *path; + const char *data; + }; + const struct linux_mtd_fread_mock_entry fread_mock_map[] = { + { "/sys/class/mtd/mtd0//type", "nor" }, + { "/sys/class/mtd/mtd0//name", "Device" }, + { "/sys/class/mtd/mtd0//flags", "" }, + { "/sys/class/mtd/mtd0//size", "1024" }, + { "/sys/class/mtd/mtd0//erasesize", "512" }, + { "/sys/class/mtd/mtd0//numeraseregions", "0" }, + }; + + struct linux_mtd_io_state *io_state = state; + unsigned int i; + + if (!io_state->fopen_path) + return 0; + + for (i = 0; i < ARRAY_SIZE(fread_mock_map); i++) { + const struct linux_mtd_fread_mock_entry *entry = &fread_mock_map[i]; + + if (!strcmp(io_state->fopen_path, entry->path)) { + size_t data_len = min(size * len, strlen(entry->data)); + memcpy(buf, entry->data, data_len); + return data_len; + } + } + + return 0; +} + +int linux_mtd_fclose(void *state, FILE *fp) +{ + struct linux_mtd_io_state *io_state = state; + + free(io_state->fopen_path); + + return 0; +} + +void linux_mtd_init_and_shutdown_test_success(void **state) +{ +#if CONFIG_LINUX_MTD == 1 + struct linux_mtd_io_state linux_mtd_io_state = { NULL }; + const struct io_mock linux_mtd_io = { + .state = &linux_mtd_io_state, + .fopen = linux_mtd_fopen, + .fread = linux_mtd_fread, + .fclose = linux_mtd_fclose, + }; + + io_mock_register(&linux_mtd_io); + + run_lifecycle(state, &programmer_linux_mtd, ""); + + io_mock_register(NULL); +#else + skip(); +#endif +} + +char *linux_spi_fgets(void *state, char *buf, int len, FILE *fp) +{ + /* Emulate reading max buffer size from sysfs. */ + const char *max_buf_size = "1048576"; + + return memcpy(buf, max_buf_size, min(len, strlen(max_buf_size) + 1)); +} + void linux_spi_init_and_shutdown_test_success(void **state) { /* * Current implementation tests a particular path of the init procedure. - * There are two ways for it to succeed: reading the buffer size from sysfs - * and the fallback to getpagesize(). This test does the latter (fallback to - * getpagesize). + * Specifically, it is reading the buffer size from sysfs. */ #if CONFIG_LINUX_SPI == 1 + const struct io_mock linux_spi_io = { + .fgets = linux_spi_fgets, + }; + + io_mock_register(&linux_spi_io); + run_lifecycle(state, &programmer_linux_spi, "dev=/dev/null"); + + io_mock_register(NULL); #else skip(); #endif diff --git a/tests/io_mock.h b/tests/io_mock.h index c69cd38..adb5f3b 100644 --- a/tests/io_mock.h +++ b/tests/io_mock.h @@ -31,6 +31,9 @@ #ifndef _IO_MOCK_H_ #define _IO_MOCK_H_
+/* Required for `FILE *` */ +#include <stdio.h> + /* Define libusb symbols to avoid dependency on libusb.h */ struct libusb_device_handle; typedef struct libusb_device_handle libusb_device_handle; @@ -79,6 +82,12 @@ int (*ioctl)(void *state, int fd, unsigned long request, va_list args); int (*read)(void *state, int fd, void *buf, size_t sz); int (*write)(void *state, int fd, const void *buf, size_t sz); + + /* Standard I/O */ + FILE* (*fopen)(void *state, const char *pathname, const char *mode); + char* (*fgets)(void *state, char *buf, int len, FILE *fp); + size_t (*fread)(void *state, void *buf, size_t size, size_t len, FILE *fp); + int (*fclose)(void *state, FILE *fp); };
void io_mock_register(const struct io_mock *io); diff --git a/tests/meson.build b/tests/meson.build index 63fec5a..53885a8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -39,6 +39,15 @@ '-Wl,--wrap=write', '-Wl,--wrap=fopen', '-Wl,--wrap=fopen64', + '-Wl,--wrap=stat', + '-Wl,--wrap=stat64', + '-Wl,--wrap=fread', + '-Wl,--wrap=fgets', + '-Wl,--wrap=fclose', + '-Wl,--wrap=feof', + '-Wl,--wrap=ferror', + '-Wl,--wrap=clearerr', + '-Wl,--wrap=setvbuf', '-Wl,--wrap=rget_io_perms', '-Wl,--wrap=test_outb', '-Wl,--wrap=test_inb', diff --git a/tests/tests.c b/tests/tests.c index bfc0e53..4965fe1 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -129,15 +129,78 @@ FILE *__wrap_fopen(const char *pathname, const char *mode) { LOG_ME; - return NULL; + if (current_io && current_io->fopen) + return current_io->fopen(current_io->state, pathname, mode); + return (void *)MOCK_HANDLE; }
FILE *__wrap_fopen64(const char *pathname, const char *mode) { LOG_ME; + if (current_io && current_io->fopen) + return current_io->fopen(current_io->state, pathname, mode); + return (void *)MOCK_HANDLE; +} + +int __wrap_stat(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap_stat64(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +char *__wrap_fgets(char *buf, int len, FILE *fp) +{ + LOG_ME; + if (current_io && current_io->fgets) + return current_io->fgets(current_io->state, buf, len, fp); return NULL; }
+size_t __wrap_fread(void *ptr, size_t size, size_t len, FILE *fp) +{ + LOG_ME; + if (current_io && current_io->fread) + return current_io->fread(current_io->state, ptr, size, len, fp); + return 0; +} + +int __wrap_setvbuf(FILE *fp, char *buf, int type, size_t size) +{ + LOG_ME; + return 0; +} + +int __wrap_fclose(FILE *fp) +{ + LOG_ME; + if (current_io && current_io->fclose) + return current_io->fclose(current_io->state, fp); + return 0; +} + +int __wrap_feof(FILE *fp) +{ + /* LOG_ME; */ + return 0; +} + +int __wrap_ferror(FILE *fp) +{ + /* LOG_ME; */ + return 0; +} +void __wrap_clearerr(FILE *fp) +{ + /* LOG_ME; */ + return; +} + int __wrap_rget_io_perms(void) { LOG_ME; @@ -277,6 +340,7 @@ cmocka_unit_test(nicrealtek_init_and_shutdown_test_success), cmocka_unit_test(dediprog_init_and_shutdown_test_success), cmocka_unit_test(ene_lpc_init_and_shutdown_test_success), + cmocka_unit_test(linux_mtd_init_and_shutdown_test_success), cmocka_unit_test(linux_spi_init_and_shutdown_test_success), cmocka_unit_test(realtek_mst_init_and_shutdown_test_success), }; diff --git a/tests/tests.h b/tests/tests.h index 512b72d..df4a41c 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -46,6 +46,7 @@ void nicrealtek_init_and_shutdown_test_success(void **state); void dediprog_init_and_shutdown_test_success(void **state); void ene_lpc_init_and_shutdown_test_success(void **state); +void linux_mtd_init_and_shutdown_test_success(void **state); void linux_spi_init_and_shutdown_test_success(void **state); void realtek_mst_init_and_shutdown_test_success(void **state);