From 05b59d2ca30548f8460d87dbf8353ab1437cb204 Mon Sep 17 00:00:00 2001 From: Anastasia Klimchuk Date: Thu, 19 Aug 2021 15:15:19 +1000 Subject: 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 Reviewed-on: https://review.coreboot.org/c/flashrom/+/56413 Tested-by: build bot (Jenkins) Reviewed-by: Nico Huber Reviewed-by: Edward O'Callaghan --- tests/init_shutdown.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-- tests/io_mock.h | 9 +++++ tests/meson.build | 9 +++++ tests/tests.c | 66 +++++++++++++++++++++++++++++++++- tests/tests.h | 1 + 5 files changed, 180 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/init_shutdown.c b/tests/init_shutdown.c index d1d0c7f3..3236b22c 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 @@ void ene_lpc_init_and_shutdown_test_success(void **state) #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 c69cd38d..adb5f3b3 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 + /* 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 @@ struct io_mock { 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 63fec5aa..53885a83 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -39,6 +39,15 @@ mocks = [ '-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 bfc0e53c..4965fe1c 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -129,15 +129,78 @@ int __wrap_read(int fd, void *buf, size_t sz) 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 @@ int main(void) 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 512b72d0..df4a41cc 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -46,6 +46,7 @@ void mec1308_init_and_shutdown_test_success(void **state); 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); -- cgit v1.2.3