From ba9890976c884f932f7c76fcdfef25173fa9f85c Mon Sep 17 00:00:00 2001 From: Hiroyuki OYAMA Date: Thu, 13 Jun 2024 11:57:16 +0900 Subject: [PATCH] Split unit testing and integration testing --- tests/CMakeLists.txt | 43 +- tests/README.md | 15 + tests/integration/CMakeLists.txt | 33 + tests/integration/main.c | 26 + tests/integration/test_blockdevice.c | 243 ++++ .../test_copy_between_different_filesystems.c | 190 ++++ tests/integration/test_filesystem.c | 416 +++++++ tests/integration/test_stdio.c | 1008 +++++++++++++++++ tests/integration/test_vfs.c | 675 +++++++++++ tests/multicore/CMakeLists.txt | 26 + tests/{multicore.c => multicore/main.c} | 0 tests/test_blockdevice.c | 61 +- .../test_copy_between_different_filesystems.c | 40 +- tests/test_filesystem.c | 33 +- tests/test_stdio.c | 21 +- tests/test_vfs.c | 79 +- 16 files changed, 2710 insertions(+), 199 deletions(-) create mode 100644 tests/README.md create mode 100644 tests/integration/CMakeLists.txt create mode 100644 tests/integration/main.c create mode 100644 tests/integration/test_blockdevice.c create mode 100644 tests/integration/test_copy_between_different_filesystems.c create mode 100644 tests/integration/test_filesystem.c create mode 100644 tests/integration/test_stdio.c create mode 100644 tests/integration/test_vfs.c create mode 100644 tests/multicore/CMakeLists.txt rename tests/{multicore.c => multicore/main.c} (100%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b8c2c01..d3a0893 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,6 @@ set(CMAKE_BUILD_TYPE Debug) -add_executable(tests +add_executable(unittests main.c test_blockdevice.c test_filesystem.c @@ -8,48 +8,27 @@ add_executable(tests test_stdio.c test_copy_between_different_filesystems.c ) -target_compile_options(tests PRIVATE -Werror -Wall -Wextra -Wnull-dereference) -target_link_libraries(tests PRIVATE +target_compile_options(unittests PRIVATE -Werror -Wall -Wextra -Wnull-dereference) +target_link_libraries(unittests PRIVATE pico_stdlib - blockdevice_flash blockdevice_heap - blockdevice_sd blockdevice_loopback filesystem_fat filesystem_littlefs filesystem_vfs ) -target_link_options(tests PRIVATE -Wl,--print-memory-usage) -pico_add_extra_outputs(tests) -pico_enable_stdio_usb(tests 1) +target_link_options(unittests PRIVATE -Wl,--print-memory-usage) +pico_add_extra_outputs(unittests) +pico_enable_stdio_usb(unittests 1) -add_executable(multicore multicore.c) -target_compile_options(multicore PRIVATE -Werror -Wall -Wextra -Wnull-dereference) -target_link_libraries(multicore PRIVATE - pico_stdlib - pico_multicore - blockdevice_flash - blockdevice_heap - blockdevice_sd - filesystem_fat - filesystem_littlefs - filesystem_vfs -) -target_link_options(multicore PRIVATE -Wl,--print-memory-usage) -pico_add_extra_outputs(multicore) -pico_enable_stdio_uart(multicore 1) -pico_enable_stdio_usb(multicore 0) -pico_set_binary_type(multicore copy_to_ram) +add_subdirectory(integration) +add_subdirectory(multicore) find_program(OPENOCD openocd) if(OPENOCD) - add_custom_target(run_tests - COMMAND ${OPENOCD} -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program tests.elf verify reset exit" - DEPENDS tests - ) - add_custom_target(run_multicore - COMMAND ${OPENOCD} -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program multicore.elf verify reset exit" - DEPENDS multicore + add_custom_target(run_unittests + COMMAND ${OPENOCD} -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program unittests.elf verify reset exit" + DEPENDS unittests ) endif() diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..4ad74e1 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,15 @@ +# Tests + +The project has three tests: + +1. unit tests `unittests` using heap memory +2. integration tests `integrate` using Raspberry Pi Pico hardware +3. `multicore` using multi-core + +```bash +cd build +make unittests integration multicore +make run_unittests +make run_integration +make run_multicore +``` diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt new file mode 100644 index 0000000..5a646ef --- /dev/null +++ b/tests/integration/CMakeLists.txt @@ -0,0 +1,33 @@ +set(CMAKE_BUILD_TYPE Debug) + +add_executable(integration + main.c + test_blockdevice.c + test_filesystem.c + test_vfs.c + test_stdio.c + test_copy_between_different_filesystems.c +) +target_compile_options(integration PRIVATE -Werror -Wall -Wextra -Wnull-dereference) +target_link_libraries(integration PRIVATE + pico_stdlib + blockdevice_flash + blockdevice_heap + blockdevice_sd + blockdevice_loopback + filesystem_fat + filesystem_littlefs + filesystem_vfs +) +target_link_options(integration PRIVATE -Wl,--print-memory-usage) +pico_add_extra_outputs(integration) +pico_enable_stdio_usb(integration 1) + + +find_program(OPENOCD openocd) +if(OPENOCD) + add_custom_target(run_integration + COMMAND ${OPENOCD} -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program integration.elf verify reset exit" + DEPENDS integration + ) +endif() diff --git a/tests/integration/main.c b/tests/integration/main.c new file mode 100644 index 0000000..49fdae0 --- /dev/null +++ b/tests/integration/main.c @@ -0,0 +1,26 @@ +#include +#include + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") + +extern void test_blockdevice(void); +extern void test_filesystem(void); +extern void test_vfs(void); +extern void test_stdio(void); +extern void test_copy_between_different_filesystems(void); + +int main(void) { + stdio_init_all(); + + printf("Start all tests\n"); + + test_blockdevice(); + test_filesystem(); + test_vfs(); + test_stdio(); + test_copy_between_different_filesystems(); + + printf(COLOR_GREEN("All tests are ok\n")); + while (1) + tight_loop_contents(); +} diff --git a/tests/integration/test_blockdevice.c b/tests/integration/test_blockdevice.c new file mode 100644 index 0000000..1beca45 --- /dev/null +++ b/tests/integration/test_blockdevice.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "blockdevice/heap.h" +#include "blockdevice/loopback.h" +#include "blockdevice/sd.h" +#include "filesystem/fat.h" +#include "filesystem/vfs.h" + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") +#define FLASH_START_AT (0.5 * 1024 * 1024) +#define FLASH_LENGTH_ALL 0 +#define HEAP_STORAGE_SIZE (512 * 128) // 64KB +#define LOOPBACK_STORAGE_SIZE 1024 +#define LOOPBACK_BLOCK_SIZE 512 + +#include +static void print_hex(const char *label, const void *buffer, size_t length) { + const uint8_t *buf = buffer; + printf("%s:\n", label); + size_t offset = 0; + for (size_t i = 0; i < length; ++i) { + if (i % 16 == 0) + printf("0x%04u%s", offset, (i % 512) == 0 ? ">" : " "); + if (isalnum(buf[i])) { + printf("'%c' ", buf[i]); + } else { + printf("0x%02x", buf[i]); + } + if (i % 16 == 15) { + printf("\n"); + offset += 16; + } else { + printf(", "); + } + } +} + +static void test_printf(const char *format, ...) { + va_list args; + va_start(args, format); + int n = vprintf(format, args); + va_end(args); + + printf(" "); + for (size_t i = 0; i < 50 - (size_t)n; i++) + printf("."); +} + +static void setup(blockdevice_t *device) { + (void)device; +} + +static void cleanup(blockdevice_t *device) { + size_t length = device->size(device); + device->erase(device, 0, length); +} + +static bool is_sd_card_connected(blockdevice_t *device) { + int err = device->deinit(device); + assert(err == BD_ERROR_OK); + + err = device->init(device); + if (err == BD_ERROR_OK) + return true; + else if (err == -5005) + return false; + else + assert(err == BD_ERROR_OK); + return false; +} + +static void test_api_init(blockdevice_t *device) { + test_printf("init"); + + // The init is done when the object is created. + int err = device->deinit(device); + assert(err == BD_ERROR_OK); + + // Init for later testing + err = device->init(device); + assert(err == BD_ERROR_OK); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_sync(blockdevice_t *device) { + test_printf("sync"); + + int err = device->sync(device); + assert(err == BD_ERROR_OK); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_erase_program_read(blockdevice_t *device) { + test_printf("erase,program,read"); + + size_t addr = 0; + size_t length = device->erase_size; + + // erase test block + int err = device->erase(device, addr, length); + assert(err == BD_ERROR_OK); + + // program by random data + uint8_t *program_buffer = calloc(1, length); + srand(length); + for (size_t i = 0; i < length; i++) + program_buffer[i] = rand() & 0xFF; + err = device->program(device, program_buffer, addr, length); + assert(err == BD_ERROR_OK); + + // read test block + uint8_t *read_buffer = calloc(1, length); + err = device->read(device, read_buffer, addr, length); + assert(err == BD_ERROR_OK); + assert(memcmp(program_buffer, read_buffer, length) == 0); + + free(read_buffer); + free(program_buffer); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_trim(blockdevice_t *device) { + test_printf("trim"); + + int err = device->trim(device, 0, 0); + assert(err == BD_ERROR_OK); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_size(blockdevice_t *device) { + test_printf("size"); + + size_t length = device->size(device); + assert(length > 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_attribute(blockdevice_t *device) { + test_printf("attribute"); + + assert(device->read_size > 0); + assert(device->erase_size > 0); + assert(device->program_size > 0); + assert(strlen(device->name) > 0); + + assert(device->config != NULL); + + printf(COLOR_GREEN("ok\n")); +} + +void test_blockdevice(void) { + printf("Block device Onboard-Flash:\n"); + blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + assert(flash != NULL); + setup(flash); + + test_api_init(flash); + test_api_erase_program_read(flash); + test_api_trim(flash); + test_api_sync(flash); + test_api_size(flash); + test_api_attribute(flash); + + cleanup(flash); + blockdevice_flash_free(flash); + + printf("Block device SPI SD card:\n"); + blockdevice_t *sd = blockdevice_sd_create(spi0, + PICO_DEFAULT_SPI_TX_PIN, + PICO_DEFAULT_SPI_RX_PIN, + PICO_DEFAULT_SPI_SCK_PIN, + PICO_DEFAULT_SPI_CSN_PIN, + 10 * MHZ, + false); + assert(sd != NULL); + if (is_sd_card_connected(sd)) { + setup(sd); + + test_api_init(sd); + test_api_erase_program_read(sd); + test_api_trim(sd); + test_api_sync(sd); + test_api_size(sd); + test_api_attribute(sd); + + cleanup(sd); + } else { + test_printf("init"); + printf("skip, device not connected\n"); + } + blockdevice_sd_free(sd); + + printf("Block device Heap memory:\n"); + blockdevice_t *heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); + setup(heap); + + test_api_init(heap); + test_api_erase_program_read(heap); + test_api_trim(heap); + test_api_sync(heap); + test_api_size(heap); + test_api_attribute(heap); + + cleanup(heap); + blockdevice_heap_free(heap); + + printf("Block device Loopback:\n"); + heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); + setup(heap); + filesystem_t *fat = filesystem_fat_create(); + assert(fat != NULL); + fs_format(fat, heap); + fs_mount("/", fat, heap); + blockdevice_t *loopback = blockdevice_loopback_create("/loopback", + LOOPBACK_STORAGE_SIZE, + LOOPBACK_BLOCK_SIZE); + assert(loopback != NULL); + + test_api_init(loopback); + test_api_erase_program_read(loopback); + test_api_trim(loopback); + test_api_sync(loopback); + test_api_size(loopback); + test_api_attribute(loopback); + + cleanup(loopback); + blockdevice_loopback_free(loopback); + fs_unmount("/"); + filesystem_fat_free(fat); + cleanup(heap); + blockdevice_heap_free(heap); +} diff --git a/tests/integration/test_copy_between_different_filesystems.c b/tests/integration/test_copy_between_different_filesystems.c new file mode 100644 index 0000000..abb7e17 --- /dev/null +++ b/tests/integration/test_copy_between_different_filesystems.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "blockdevice/sd.h" +#include "filesystem/littlefs.h" +#include "filesystem/fat.h" +#include "filesystem/vfs.h" + + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") + +struct combination_map { + blockdevice_t *device1; + filesystem_t *filesystem1; + blockdevice_t *device2; + filesystem_t *filesystem2; +}; + +#define NUM_COMBINATION 6 +static struct combination_map combination[NUM_COMBINATION]; + + +static void init_filesystem_combination(void) { + blockdevice_t *flash1 = blockdevice_flash_create(1 * 1024 * 1024, 512 * 1024); + blockdevice_t *flash2 = blockdevice_flash_create(1 * 1024 * 1024 + 512 * 1024, 0); + blockdevice_t *sd = blockdevice_sd_create(spi0, + PICO_DEFAULT_SPI_TX_PIN, + PICO_DEFAULT_SPI_RX_PIN, + PICO_DEFAULT_SPI_SCK_PIN, + PICO_DEFAULT_SPI_CSN_PIN, + 24 * MHZ, + true); + filesystem_t *fat1 = filesystem_fat_create(); + filesystem_t *fat2 = filesystem_fat_create(); + filesystem_t *littlefs1 = filesystem_littlefs_create(500, 16); + filesystem_t *littlefs2 = filesystem_littlefs_create(500, 16); + + combination[0] = (struct combination_map){ + .device1 = flash1, .filesystem1 = fat1, .device2 = flash2, .filesystem2 = fat2, + }; + combination[1] = (struct combination_map){ + .device1 = flash1, .filesystem1 = fat1, .device2 = flash2, .filesystem2 = littlefs2, + }; + combination[2] = (struct combination_map){ + .device1 = flash1, .filesystem1 = littlefs1, .device2 = flash2, .filesystem2 = littlefs2, + }; + combination[3] = (struct combination_map){ + .device1 = flash1, .filesystem1 = fat1, .device2 = sd, .filesystem2 = fat2, + }; + combination[4] = (struct combination_map){ + .device1 = flash1, .filesystem1 = fat1, .device2 = sd, .filesystem2 = littlefs2, + }; + combination[5] = (struct combination_map){ + .device1 = flash1, .filesystem1 = littlefs1, .device2 = sd, .filesystem2 = littlefs2, + }; +} + +#define TEST_FILE_SIZE 100 * 1024; + +static void test_printf(const char *format, ...) { + va_list args; + va_start(args, format); + int n = vprintf(format, args); + va_end(args); + + printf(" "); + for (size_t i = 0; i < 50 - (size_t)n; i++) + printf("."); +} + +static void test_write(const char *path) { + + int fd = open(path, O_WRONLY|O_CREAT); + assert(fd >= 0); + + uint8_t buffer[1024*64] = {0}; + size_t remind = TEST_FILE_SIZE; + while (remind > 0) { + size_t chunk = remind % sizeof(buffer) ? remind % sizeof(buffer) : sizeof(buffer); + for (size_t i = 0; i < chunk; i++) + buffer[i] = rand() & 0xFF; + ssize_t write_size = write(fd, buffer, chunk); + assert(write_size > 0); + + remind = remind - write_size; + } + int err = close(fd); + assert(err == 0); +} + +static void test_copy(const char *source, const char *dist) { + int fd_src = open(source, O_RDONLY); + assert(fd_src >= 0); + int fd_dist = open(dist, O_WRONLY|O_CREAT); + assert(fd_dist >= 0); + assert(fd_src != fd_dist); + + uint8_t buffer[1024*64] = {0}; + while (true) { + ssize_t read_size = read(fd_src, buffer, sizeof(buffer)); + if (read_size == 0) + break; + ssize_t write_size = write(fd_dist, buffer, read_size); + assert(read_size == write_size); + } + + int err = close(fd_src); + assert(err == 0); + err = close(fd_dist); + assert(err == 0); +} + +static void test_read(const char *path) { + + int fd = open(path, O_RDONLY); + assert(fd >= 0); + + uint8_t buffer[1024*64] = {0}; + while (true) { + ssize_t read_size = read(fd, buffer, sizeof(buffer)); + if (read_size == 0) + break; + for (size_t i = 0; i < (size_t)read_size; i++) { + volatile uint8_t r = rand() & 0xFF; + assert(buffer[i] == r); + } + } + + int err = close(fd); + assert(err == 0); +} + +void test_copy_between_different_filesystems(void) { + printf("Copy between different file system:\n"); + + init_filesystem_combination(); + + for (size_t i = 0; i < NUM_COMBINATION; i++) { + struct combination_map setting = combination[i]; + test_printf("from %s(%s) to %s(%s)", + setting.filesystem1->name, setting.device1->name, + setting.filesystem2->name, setting.device2->name); + + int err = fs_format(setting.filesystem1, setting.device1); + if (err == -1 && errno == 5005) { + printf("skip, device not connected\n"); + continue; + } + assert(err == 0); + err = fs_format(setting.filesystem2, setting.device2); + if (err == -1 && errno == 5005) { + printf("skip, device not connected\n"); + continue; + } + assert(err == 0); + + err = fs_mount("/a", setting.filesystem1, setting.device1); + assert(err == 0); + err = fs_mount("/b", setting.filesystem2, setting.device2); + assert(err == 0); + + srand(i); + test_write("/a/source"); + test_copy("/a/source", "/b/dist"); + srand(i); + test_read("/b/dist"); + printf(COLOR_GREEN("ok\n")); + + test_printf("from %s(%s) to %s(%s)", + setting.filesystem2->name, setting.device2->name, + setting.filesystem1->name, setting.device1->name), + + srand(i); + test_write("/b/source"); + test_copy("/b/source", "/a/dist"); + srand(i); + test_read("/a/dist"); + + err = fs_unmount("/a"); + assert(err == 0); + err = fs_unmount("/b"); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); + } +} diff --git a/tests/integration/test_filesystem.c b/tests/integration/test_filesystem.c new file mode 100644 index 0000000..dd8f2bc --- /dev/null +++ b/tests/integration/test_filesystem.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "filesystem/fat.h" +#include "filesystem/littlefs.h" + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") +#define FLASH_START_AT (0.5 * 1024 * 1024) +#define FLASH_LENGTH_ALL 0 +#define LITTLEFS_BLOCK_CYCLE 500 +#define LITTLEFS_LOOKAHEAD_SIZE 16 + + +static void test_printf(const char *format, ...) { + va_list args; + va_start(args, format); + int n = vprintf(format, args); + va_end(args); + + printf(" "); + for (size_t i = 0; i < 50 - (size_t)n; i++) + printf("."); +} + +static void setup(blockdevice_t *device) { + (void)device; +} + +static void cleanup(blockdevice_t *device) { + size_t length = device->size(device); + // Deinit is performed when unmounting, so re-init is required. + device->init(device); + device->erase(device, 0, length); +} + +static void test_api_format(filesystem_t *fs, blockdevice_t *device) { + test_printf("format"); + + int err = fs->format(fs, device); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_mount(filesystem_t *fs, blockdevice_t *device) { + test_printf("mount"); + + int err = fs->mount(fs, device, false); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_unmount(filesystem_t *fs) { + test_printf("unmount"); + + int err = fs->unmount(fs); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_open_close(filesystem_t *fs) { + test_printf("file_open,file_close"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_RDONLY); // non-existing file + assert(err == -ENOENT); + + err = fs->file_open(fs, &file, "/file", O_WRONLY|O_CREAT); + assert(err == 0); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_write_read(filesystem_t *fs) { + test_printf("file_write,file_read"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_WRONLY|O_CREAT); + assert(err == 0); + + char write_buffer[512] = "Hello World!"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + err = fs->file_close(fs, &file); + assert(err == 0); + + err = fs->file_open(fs, &file, "/file", O_RDONLY); + assert(err == 0); + char read_buffer[512] = {0}; + ssize_t read_length = fs->file_read(fs, &file, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(read_buffer, write_buffer) == 0); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_seek(filesystem_t *fs) { + test_printf("file_seek"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_RDWR|O_CREAT); + assert(err == 0); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t offset = fs->file_seek(fs, &file, 0, SEEK_SET); + assert(offset == 0); + + char read_buffer[512] = {0}; + ssize_t read_length = fs->file_read(fs, &file, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(write_buffer, read_buffer) == 0); + + offset = fs->file_seek(fs, &file, 9, SEEK_SET); + assert(offset == 9); + memset(read_buffer, 0, sizeof(read_buffer)); + read_length = fs->file_read(fs, &file, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer) - 9); + assert(strcmp("ABCDEF", read_buffer) == 0); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_tell(filesystem_t *fs) { + test_printf("file_tell"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_RDWR|O_CREAT); + assert(err == 0); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t offset = fs->file_seek(fs, &file, 0, SEEK_SET); + assert(offset == 0); + + off_t pos = fs->file_tell(fs, &file); + assert(pos == 0); + + offset = fs->file_seek(fs, &file, 0, SEEK_END); + assert((size_t)offset == strlen(write_buffer)); + + pos = fs->file_tell(fs, &file); + assert((size_t)pos == strlen(write_buffer)); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_size(filesystem_t *fs) { + test_printf("file_size"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_RDWR|O_CREAT); + assert(err == 0); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t size = fs->file_size(fs, &file); + assert((size_t)size == strlen(write_buffer)); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_truncate(filesystem_t *fs) { + test_printf("file_truncate"); + + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_RDWR|O_CREAT); + assert(err == 0); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t offset = fs->file_seek(fs, &file, 0, SEEK_SET); + assert(offset == 0); + + err = fs->file_truncate(fs, &file, 9); + assert(err == 0); + + char read_buffer[512] = {0}; + ssize_t read_length = fs->file_read(fs, &file, read_buffer, sizeof(read_buffer)); + assert(read_length == 9); + assert(strcmp(read_buffer, "123456789") == 0); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_dir_open(filesystem_t *fs) { + test_printf("dir_open,dir_close"); + + fs_dir_t dir; + int err = fs->dir_open(fs, &dir, "/dir"); // non-exists directory + assert(err == -ENOTDIR || err == -ENOENT); + + err = fs->mkdir(fs, "/dir", 0777); + assert(err == 0); + + err = fs->dir_open(fs, &dir, "/dir"); + assert(err == 0); + + err = fs->dir_close(fs, &dir); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_dir_read(filesystem_t *fs) { + test_printf("dir_read"); + + int err = fs->mkdir(fs, "/dir", 0777); + assert(err == 0 || err == -EEXIST); + + fs_file_t file; + err = fs->file_open(fs, &file, "/dir/file", O_WRONLY|O_CREAT); + assert(err == 0); + err = fs->file_close(fs, &file); + assert(err == 0); + + fs_dir_t dir; + err = fs->dir_open(fs, &dir, "/dir"); + assert(err == 0); + + struct dirent ent; + + if (fs->type != FILESYSTEM_TYPE_FAT) { // FAT does not return dot entries + err = fs->dir_read(fs, &dir, &ent); + assert(err == 0); + assert(ent.d_type == DT_DIR); + assert(strcmp(ent.d_name, ".") == 0); + + err = fs->dir_read(fs, &dir, &ent); + assert(err == 0); + assert(ent.d_type == DT_DIR); + assert(strcmp(ent.d_name, "..") == 0); + } + err = fs->dir_read(fs, &dir, &ent); + assert(err == 0); + assert(ent.d_type == DT_REG); + assert(strcmp(ent.d_name, "file") == 0); + + err = fs->dir_read(fs, &dir, &ent); // Reach the end of the directory + assert(err != 0); + + err = fs->dir_close(fs, &dir); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_remove(filesystem_t *fs) { + test_printf("remove"); + + int err = fs->remove(fs, "/not-exists"); + assert(err == -ENOENT); + + fs_file_t file; + err = fs->file_open(fs, &file, "/file", O_WRONLY|O_CREAT); + assert(err == 0); + err = fs->file_close(fs, &file); + assert(err == 0); + + err = fs->remove(fs, "/file"); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_rename(filesystem_t *fs) { + test_printf("rename"); + + int err = fs->rename(fs, "/not-exists", "/renamed"); + assert(err == -ENOENT); + + fs_file_t file; + err = fs->file_open(fs, &file, "/file", O_WRONLY|O_CREAT); + assert(err == 0); + char write_buffer[512] = "Hello World!"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + err = fs->file_close(fs, &file); + assert(err == 0); + + err = fs->rename(fs, "/file", "/renamed"); + assert(err == 0); + + err = fs->file_open(fs, &file, "/renamed", O_RDONLY); + assert(err == 0); + char read_buffer[512] = {0}; + ssize_t read_length = fs->file_read(fs, &file, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(read_buffer, write_buffer) == 0); + + err = fs->file_close(fs, &file); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_stat(filesystem_t *fs) { + test_printf("stat"); + + // regular file + fs_file_t file; + int err = fs->file_open(fs, &file, "/file", O_WRONLY|O_CREAT); + assert(err == 0); + char write_buffer[512] = "Hello World!"; + ssize_t write_length = fs->file_write(fs, &file, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + err = fs->file_close(fs, &file); + assert(err == 0); + + struct stat finfo; + err = fs->stat(fs, "/file", &finfo); + assert(err == 0); + assert((size_t)finfo.st_size == strlen(write_buffer)); + assert(finfo.st_mode & S_IFREG); + + // directory + err = fs->mkdir(fs, "/dir", 0777); + assert(err == 0 || err == -EEXIST); + err = fs->stat(fs, "/dir", &finfo); + assert(err == 0); + assert(finfo.st_mode & S_IFDIR); + + printf(COLOR_GREEN("ok\n")); +} + +void test_filesystem(void) { + printf("File system FAT:\n"); + + blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + + assert(flash != NULL); + filesystem_t *fat = filesystem_fat_create(); + assert(fat != NULL); + setup(flash); + + test_api_format(fat, flash); + test_api_mount(fat, flash); + test_api_file_open_close(fat); + test_api_file_write_read(fat); + test_api_file_seek(fat); + test_api_file_tell(fat); + test_api_file_size(fat); + test_api_file_truncate(fat); + test_api_dir_open(fat); + test_api_dir_read(fat); + test_api_remove(fat); + test_api_rename(fat); + test_api_stat(fat); + + test_api_unmount(fat); + cleanup(flash); + blockdevice_flash_free(flash); + filesystem_fat_free(fat); + + + printf("File system littlefs:\n"); + flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + assert(flash != NULL); + filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, + LITTLEFS_LOOKAHEAD_SIZE); + assert(lfs != NULL); + setup(flash); + + test_api_format(lfs, flash); + test_api_mount(lfs, flash); + test_api_file_open_close(lfs); + test_api_file_write_read(lfs); + test_api_file_seek(lfs); + test_api_file_tell(lfs); + test_api_file_size(lfs); + test_api_file_truncate(lfs); + test_api_dir_open(lfs); + test_api_dir_read(lfs); + test_api_remove(lfs); + test_api_rename(lfs); + test_api_stat(lfs); + + test_api_unmount(lfs); + + cleanup(flash); + blockdevice_flash_free(flash); + filesystem_littlefs_free(lfs); +} diff --git a/tests/integration/test_stdio.c b/tests/integration/test_stdio.c new file mode 100644 index 0000000..84f9b32 --- /dev/null +++ b/tests/integration/test_stdio.c @@ -0,0 +1,1008 @@ +#include +#include +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "filesystem/fat.h" +#include "filesystem/littlefs.h" +#include "filesystem/vfs.h" + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") +#define FLASH_START_AT (0.5 * 1024 * 1024) +#define FLASH_LENGTH_ALL 0 +#define LITTLEFS_BLOCK_CYCLE 500 +#define LITTLEFS_LOOKAHEAD_SIZE 16 + +static void test_printf(const char *format, ...) { + va_list args; + va_start(args, format); + int n = vprintf(format, args); + va_end(args); + + printf(" "); + for (size_t i = 0; i < 50 - (size_t)n; i++) + printf("."); +} + + +static void setup(filesystem_t *fs, blockdevice_t *device) { + int err = fs_format(fs, device); + assert(err == 0); + err = fs_mount("/", fs, device); + assert(err == 0); +} + +static void cleanup(filesystem_t *fs, blockdevice_t *device) { + (void)fs; + int err = fs_unmount("/"); + assert(err == 0); + size_t length = device->size(device); + device->erase(device, 0, length); +} + +static void test_clearerr(void) { + char buffer[512]; + test_printf("clearerr"); + + FILE *fp = fopen("/clearerr", "w+"); + assert(feof(fp) == 0); + assert(ferror(fp) == 0); + fprintf(fp, "hello world\n"); + fgets(buffer, sizeof(buffer), fp); + assert(feof(fp) != 0); + clearerr(fp); + assert(feof(fp) == 0); + fclose(fp); + + fp = fopen("/clearerr", "r"); + fprintf(fp, "can't write stream\n"); + assert(ferror(fp) != 0); + clearerr(fp); + assert(ferror(fp) == 0); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fflush(void) { + test_printf("fflush"); + + FILE *fp = fopen("/fflush", "w+"); + fprintf(fp, "hello world"); + int fd = fileno(fp); + struct stat st; + int err = fstat(fd, &st); + assert(err == 0); + assert(st.st_size == 0); + + fflush(fp); + err = fstat(fd, &st); + assert(err == 0); + assert(st.st_size > 0); + + close(fd); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fgetc(void) { + test_printf("fgetc"); + + FILE *fp = fopen("/fgetc", "w+"); + fprintf(fp, "012AB"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + assert(fgetc(fp) == '0'); + assert(fgetc(fp) == '1'); + assert(fgetc(fp) == '2'); + assert(fgetc(fp) == 'A'); + assert(fgetc(fp) == 'B'); + assert(fgetc(fp) == EOF); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fgetpos(void) { + test_printf("fgetpos"); + + FILE *fp = fopen("/fgetpos", "w+"); + fprintf(fp, "012AB"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + fpos_t pos; + int err = fgetpos(fp, &pos); + assert(err == 0); + assert(pos == 0); + + fseek(fp, 0, SEEK_END); + err = fgetpos(fp, &pos); + assert(err == 0); + assert(pos == strlen("012AB")); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fgets(void) { + test_printf("fgets"); + + FILE *fp = fopen("/fgets", "w+"); + fprintf(fp, "0123456789\nABCDEF\n"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + char buffer[512]; + char *line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "0123456789\n") == 0); + assert(strcmp(buffer, "0123456789\n") == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "ABCDEF\n") == 0); + assert(strcmp(buffer, "ABCDEF\n") == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(line == NULL); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fgetwc(void) { + test_printf("fgetwc"); + + FILE *fp = fopen("/fgetwc", "w+"); + fwprintf(fp, L"WCHAR"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + assert(fgetwc(fp) == L'W'); + assert(fgetwc(fp) == L'C'); + assert(fgetwc(fp) == L'H'); + assert(fgetwc(fp) == L'A'); + assert(fgetwc(fp) == L'R'); + assert(fgetwc(fp) == WEOF); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fgetws(void) { + test_printf("fgetws"); + + FILE *fp = fopen("/fgetws", "w+"); + fwprintf(fp, L"WCHAR\nSTRINGS\n"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + wchar_t buffer[512]; + wchar_t *line = fgetws(buffer, sizeof(buffer), fp); + assert(wcscmp(line, L"WCHAR\n") == 0); + assert(wcscmp(buffer, L"WCHAR\n") == 0); + line = fgetws(buffer, sizeof(buffer), fp); + assert(wcscmp(line, L"STRINGS\n") == 0); + assert(wcscmp(buffer, L"STRINGS\n") == 0); + + line = fgetws(buffer, sizeof(buffer), fp); + assert(line == NULL); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fileno(void) { + test_printf("fileno"); + + FILE *fp = fopen("/fileno", "w+"); + fprintf(fp, "hello world\n"); + fflush(fp); + int fd = fileno(fp); + lseek(fd, 0, SEEK_SET); + char buffer[512] = {0}; + ssize_t len = read(fd, buffer, sizeof(buffer)); + assert(len == strlen("hello world\n")); + assert(strcmp(buffer, "hello world\n") == 0); + + close(fd); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fmemopen(void) { + test_printf("fmemopen"); + + char buffer[512] = {0}; + FILE *fp = fmemopen(buffer, sizeof(buffer), "w+"); + assert(fprintf(fp, "0123456789\n") == 11); + assert(fprintf(fp, "ABCDEF\n") == 7); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + char read_buffer[512]; + char *line = fgets(read_buffer, sizeof(read_buffer), fp); + assert(strcmp(line, "0123456789\n") == 0); + assert(strcmp(read_buffer, "0123456789\n") == 0); + + line = fgets(read_buffer, sizeof(read_buffer), fp); + assert(strcmp(line, "ABCDEF\n") == 0); + assert(strcmp(read_buffer, "ABCDEF\n") == 0); + + line = fgets(read_buffer, sizeof(read_buffer), fp); + assert(line == NULL); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fopen(void) { + test_printf("fopen"); + + FILE *fp = fopen("/fopen-not-exists", "r"); + assert(fp == NULL); + assert(errno == ENOENT); + + fp = fopen("/fopen", "w"); + assert(fp != NULL); + assert(fprintf(fp, "0123456789\n") == 11); + assert(fprintf(fp, "ABCDEF\n") == 7); + int err = fclose(fp); + assert(err == 0); + + + fp = fopen("/fopen", "r"); + assert(fp != NULL); + char buffer[512]; + char *line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "0123456789\n") == 0); + assert(strcmp(buffer, "0123456789\n") == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "ABCDEF\n") == 0); + assert(strcmp(buffer, "ABCDEF\n") == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(line == NULL); + + err = fclose(fp); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fputc(void) { + test_printf("fputc"); + + FILE *fp = fopen("/fputc", "w+"); + assert(fputc('A', fp) == 'A'); + assert(fputc('B', fp) == 'B'); + assert(fputc('C', fp) == 'C'); + + fseek(fp, 0, SEEK_SET); + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "ABC") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fputs(void) { + test_printf("fputs"); + + FILE *fp = fopen("/fputs", "w+"); + int err = fputs("Hello\n", fp); + assert(err == 0); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "Hello\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fputwc(void) { + test_printf("fputwc"); + + FILE *fp = fopen("/fputwc", "w+"); + assert(fputwc(L'W', fp) == L'W'); + assert(fputwc(L'C', fp) == L'C'); + + fseek(fp, 0, SEEK_SET); + wint_t buffer[512]; + fgetws(buffer, sizeof(buffer), fp); + assert(wcscmp(buffer, L"WC") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fputws(void) { + test_printf("fputws"); + + FILE *fp = fopen("/fputws", "w+"); + assert(fputws(L"WIDE CHAR\n", fp) == 0); + + fseek(fp, 0, SEEK_SET); + wint_t buffer[512]; + fgetws(buffer, sizeof(buffer), fp); + assert(wcscmp(buffer, L"WIDE CHAR\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fread(void) { + test_printf("fread"); + + FILE *fp = fopen("/fread", "w+"); + fprintf(fp, "Hello World\n"); + fseek(fp, 0, SEEK_SET); + + char buffer[512] = {0}; + size_t size = fread(buffer, 1, sizeof(buffer), fp); + assert(size == strlen("Hello World\n")); + assert(strcmp(buffer, "Hello World\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_freopen(void) { + test_printf("freopen"); + + FILE *fp = fopen("/freopen", "w"); + fprintf(fp, "Hello World\n"); + + fp = freopen("/freopen", "r", fp); + assert(fp != NULL); + char buffer[512]; + char *line = fgets(buffer, sizeof(buffer), fp); + assert(fp != NULL); + assert(strcmp(line, "Hello World\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fseek(void) { + test_printf("fseek"); + + FILE *fp = fopen("/fseek", "w+"); + fprintf(fp, "Hello\nWorld\n"); + + assert(fseek(fp, 6, SEEK_SET) == 0); + + char buffer[512]; + char *line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "World\n") == 0); + + assert(fseek(fp, 0, SEEK_SET) == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "Hello\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fsetpos(void) { + test_printf("fsetpos"); + + FILE *fp = fopen("/fsetpos", "w+"); + fprintf(fp, "Hello\nWorld\n"); + + fpos_t pos = 6; + assert(fsetpos(fp, &pos) == 0); + + char buffer[512]; + char *line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "World\n") == 0); + + pos = 0; + assert(fsetpos(fp, &pos) == 0); + + line = fgets(buffer, sizeof(buffer), fp); + assert(strcmp(line, "Hello\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_ftell(void) { + test_printf("ftell"); + + FILE *fp = fopen("/ftell", "w+"); + fprintf(fp, "Hello\nWorld\n"); + fseek(fp, 0, SEEK_SET); + assert(ftell(fp) == 0); + + fseek(fp, 6, SEEK_SET); + assert(ftell(fp) == 6); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fwide(void) { + test_printf("fwide"); + + FILE *fp = fopen("/fwide", "w"); + assert(fwide(fp, 0) == 0); + fprintf(fp, "byte\n"); + assert(fwide(fp, 0) < 0); + + fp = freopen(NULL, "w", fp); + assert(fwide(fp, 0) == 0); + assert(fwide(fp, 1) > 0); + + fclose(fp); + + fp = fopen("/fwide.wide", "w"); + assert(fwide(fp, 0) == 0); + fwprintf(fp, L"wide\n"); + assert(fwide(fp, 0) > 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_fwrite(void) { + test_printf("fwrite"); + + FILE *fp = fopen("/fwrite", "w+"); + uint8_t write_buffer[] = {0x01, 0x02, 0x03, 0x04}; + size_t size = fwrite(write_buffer, 1, sizeof(write_buffer), fp); + assert(size == sizeof(write_buffer)); + + fseek(fp, 0, SEEK_SET); + + uint8_t read_buffer[4] = {0}; + size = fread(read_buffer, 1, sizeof(read_buffer), fp); + assert(size == sizeof(read_buffer)); + assert(memcmp(write_buffer, read_buffer, sizeof(write_buffer)) == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_getc(void) { + test_printf("getc"); + + FILE *fp = fopen("/getc", "w+"); + fprintf(fp, "012AB"); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + assert(getc(fp) == '0'); + assert(getc(fp) == '1'); + assert(getc(fp) == '2'); + assert(getc(fp) == 'A'); + assert(getc(fp) == 'B'); + assert(getc(fp) == EOF); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_getw(void) { + test_printf("getw"); + + FILE *fp = fopen("/getw", "w+"); + int word = 536870911; + fwrite(&word, 1, sizeof(word), fp); + fflush(fp); + fseek(fp, 0, SEEK_SET); + + assert(getw(fp) == word); + assert(getw(fp) == EOF); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_mkstemp(void) { + test_printf("mkstemp"); + + FILE *fp = fopen("/mkstemp.000000", "w"); + fprintf(fp, "exists\n"); + fclose(fp); + + char path[] = "/mkstemp.XXXXXX"; + int fd = mkstemp(path); + assert(fd >= 0); + close(fd); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_open_memstream(void) { + test_printf("open_memstream"); + + char *buffer = NULL; + size_t size = 0; + FILE *fp = open_memstream(&buffer, &size); + fprintf(fp, "hello world"); + fclose(fp); + + assert(size == strlen("hello world")); + assert(memcmp(buffer, "hello world", sizeof("hello world")) == 0); + free(buffer); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_perror(void) { + test_printf("perror"); + + errno = 0; + perror(COLOR_GREEN("ok")); +} + +static void test_putc(void) { + test_printf("putc"); + + FILE *fp = fopen("/putc", "w+"); + assert(putc('A', fp) == 'A'); + assert(putc('B', fp) == 'B'); + assert(putc('C', fp) == 'C'); + + fseek(fp, 0, SEEK_SET); + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "ABC") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_remove(void) { + test_printf("remove"); + + FILE *fp = fopen("/remove", "w"); + fclose(fp); + + int err = remove("/remove"); + assert(err == 0); + + err = remove("/not-exists"); + assert(err == -1); + assert(errno == ENOENT); + + printf(COLOR_GREEN("ok\n")); +} + +void test_rename(void) { + test_printf("rename"); + + FILE *fp = fopen("/rename", "w"); + fclose(fp); + + int err = rename("/rename", "/renamed"); + assert(err == 0); + + err = rename("/not-exists", "/renamed"); + assert(err == -1); + assert(errno == ENOENT); + + printf(COLOR_GREEN("ok\n")); +} + +void test_rewind(void) { + test_printf("rewind"); + + FILE *fp = fopen("/rewind", "w+"); + fprintf(fp, "Hello World"); + rewind(fp); + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "Hello World") == 0); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_setbuf(void) { + test_printf("setbuf"); + + char buffer[BUFSIZ] = {0}; + + FILE *fp = fopen("/setbuf", "w"); + setbuf(fp, buffer); + fprintf(fp, "Hello World\n"); + fclose(fp); + assert(strcmp(buffer, "Hello World\n") == 0); + + printf(COLOR_GREEN("ok\n")); +} + +void test_setvbuf(void) { + test_printf("setvbuf"); + + char buffer[BUFSIZ]; + + // Do not use a buffer + FILE *fp = fopen("/setvbuf", "w+"); + int fd = fileno(fp); + int err = setvbuf(fp, NULL, _IONBF, 0); + assert(err == 0); + fprintf(fp, "Hello World"); + char read_buffer[512] = {0}; + + lseek(fd, 0, SEEK_SET); + ssize_t size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == strlen("Hello World")); + + close(fd); + fclose(fp); + + // use full output buffering + fp = fopen("/setvbuf", "w+"); + fd = fileno(fp); + err = setvbuf(fp, buffer, _IOFBF, 5); + assert(err == 0); + fprintf(fp, "He"); + lseek(fd, 0, SEEK_SET); + size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == 0); + + fprintf(fp, "llo W"); + lseek(fd, 0, SEEK_SET); + size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == strlen("Hello")); + + fflush(fp); + + lseek(fd, 0, SEEK_SET); + size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == strlen("Hello W")); + + close(fd); + fclose(fp); + + // use line buffering + fp = fopen("/setvbuf", "w+"); + fd = fileno(fp); + err = setvbuf(fp, buffer, _IOLBF, 128); + assert(err == 0); + fprintf(fp, "Hello"); + lseek(fd, 0, SEEK_SET); + size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == 0); + + fprintf(fp, " World\n"); + + lseek(fd, 0, SEEK_SET); + size = read(fd, read_buffer, sizeof(read_buffer)); + assert(size == strlen("Hello World\n")); + + close(fd); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_fprintf(void) { + test_printf("fprintf"); + + FILE *fp = fopen("/fprintf", "w+"); + fprintf(fp, "%s", "Hello World"); + rewind(fp); + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "Hello World") == 0); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_fscanf(void) { + test_printf("fscanf"); + + FILE *fp = fopen("/fscanf", "w+"); + fprintf(fp, "%s", "123,abc,0.987\n"); + rewind(fp); + int int_value = 0; + char strings[512]; + float float_value = 0.0; + + int assigned = fscanf(fp, "%d,%[^,],%f\n", &int_value, strings, &float_value); + assert(assigned == 3); + assert(int_value == 123); + assert(strcmp(strings, "abc") == 0); + assert(float_value < 1.0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_fwprintf(void) { + test_printf("fwprintf"); + + FILE *fp = fopen("/fwprintf", "w+"); + int size = fwprintf(fp, L"Hello World\n"); + assert(size == 12); + + fflush(fp); + rewind(fp); + wchar_t buffer[512]; + wchar_t *line = fgetws(buffer, sizeof(buffer), fp); + assert(wcscmp(line, L"Hello World\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_fwscanf(void) { + test_printf("fwscanf"); + + FILE *fp = fopen("/fwscanf", "w+"); + fwprintf(fp, L"123,abc,0.987\n"); + rewind(fp); + int int_value = 0; + char strings[512]; + float float_value = 0.0; + + int assigned = fwscanf(fp, L"%d,%[^,],%f\n", &int_value, strings, &float_value); + assert(assigned == 3); + assert(int_value == 123); + assert(strcmp(strings, "abc") == 0); + assert(float_value < 1.0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_tmpfile(void) { + test_printf("tmpfile"); + + FILE *fp = tmpfile(); + assert(fp == NULL); + + printf("not support\n"); // tmpfile also requires the global pointr environ +} + +void test_tmpnam(void) { + test_printf("tmpnam"); + + char *path = tmpnam("prefix"); + assert(path != NULL); + assert(strcmp(path, "prefix") == 0); // unintended behavior + + printf("not support\n"); // The global pointer environ is also required +} + +void test_ungetc(void) { + test_printf("ungetc"); + + FILE *fp = fopen("/ungetc", "w+"); + fprintf(fp, "12345"); + fflush(fp); + + rewind(fp); + assert(fgetc(fp) == '1'); + assert(fgetc(fp) == '2'); + assert(fgetc(fp) == '3'); + ungetc('3', fp); + assert(fgetc(fp) == '3'); + assert(fgetc(fp) == '4'); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + + +void test_ungetwc(void) { + test_printf("ungetwc"); + + FILE *fp = fopen("/ungetc", "w+"); + fwprintf(fp, L"12345"); + fflush(fp); + + rewind(fp); + assert(fgetwc(fp) == L'1'); + assert(fgetwc(fp) == L'2'); + assert(fgetwc(fp) == L'3'); + ungetwc(L'3', fp); + assert(fgetwc(fp) == L'3'); + assert(fgetwc(fp) == L'4'); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +int _test_vfprintf(FILE *fp, const char *format, ...) { + va_list args; + va_start(args, format); + int size = vfprintf(fp, format, args); + va_end(args); + return size; +} + +void test_vfprintf(void) { + test_printf("vfprintf"); + + FILE *fp = fopen("/vfprintf", "w+"); + + int size = _test_vfprintf(fp, "%s %s\n", "Hello", "World"); + assert(size == 12); + + fflush(fp); + rewind(fp); + char buffer[512]; + fgets(buffer, sizeof(buffer), fp); + assert(strcmp(buffer, "Hello World\n") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + + +int _test_vfscanf(FILE *fp, const char *format, ...) { + va_list list; + va_start(list, format); + int size = vfscanf(fp, format, list); + va_end(list); + return size; +} + +void test_vfscanf(void) { + test_printf("vfscnaf"); + + FILE *fp = fopen("/vfscanf", "w+"); + fprintf(fp, "123,ABC\n"); + fflush(fp); + rewind(fp); + + int int_value = 0; + char char_value[512]; + int number = _test_vfscanf(fp, "%d,%s\n", &int_value, char_value); + assert(number == 2); + assert(int_value == 123); + assert(strcmp(char_value, "ABC") == 0); + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +int _test_vfwscanf(FILE *fp, const wchar_t *format, ...) { + va_list list; + va_start(list, format); + int size = vfwscanf(fp, format, list); + va_end(list); + return size; +} + +void test_vfwscanf(void) { + test_printf("vfwscnaf"); + + FILE *fp = fopen("/vfwscanf", "w+"); + fwprintf(fp, L"123,ABC"); + fflush(fp); + rewind(fp); + + int int_value = 0; + char wchar_value[512] = {0}; + int number = _test_vfwscanf(fp, L"%d,%s", &int_value, wchar_value); + assert(number == 2); + assert(int_value == 123); + assert(strcmp(wchar_value, "ABC") == 0); + + fclose(fp); + + printf(COLOR_GREEN("ok\n")); +} + +void test_standard_file_api(void) { + test_clearerr(); + test_fflush(); + test_fgetc(); + test_fgetpos(); + test_fgets(); + test_fgetwc(); + test_fgetws(); + test_fileno(); + test_fmemopen(); + test_fopen(); + test_fputc(); + test_fputs(); + test_fputwc(); + test_fputws(); + test_fread(); + test_freopen(); + test_fseek(); + test_fsetpos(); + test_ftell(); + test_fwide(); + test_fwrite(); + test_getc(); + //test_getdelim(); // not support + //test_getline(); // not support + test_getw(); + //test_mktemp(); // NOTE: `mktemp' is dangerous; use `mkstemp' instead + test_mkstemp(); + test_open_memstream(); + test_perror(); + test_putc(); + //test_putc_unlocked(); + test_remove(); + test_rename(); + test_rewind(); + test_setbuf(); + test_setvbuf(); + test_fprintf(); + test_fscanf(); + test_fwprintf(); + test_fwscanf(); + test_tmpfile(); + test_tmpnam(); + test_ungetc(); + test_ungetwc(); + test_vfprintf(); + test_vfscanf(); + test_vfwscanf(); +} + + +void test_stdio(void) { + printf("POSIX and C standard file API(littlefs):\n"); + + blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, + LITTLEFS_LOOKAHEAD_SIZE); + + setup(lfs, flash); + + test_standard_file_api(); + + cleanup(lfs, flash); + filesystem_littlefs_free(lfs); + blockdevice_flash_free(flash); + + + printf("POSIX and C standard file API(FAT):\n"); + + flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + filesystem_t *fat = filesystem_fat_create(); + setup(fat, flash); + + test_standard_file_api(); + + cleanup(fat, flash); + filesystem_fat_free(fat); + blockdevice_flash_free(flash); +} diff --git a/tests/integration/test_vfs.c b/tests/integration/test_vfs.c new file mode 100644 index 0000000..54eece2 --- /dev/null +++ b/tests/integration/test_vfs.c @@ -0,0 +1,675 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "blockdevice/heap.h" +#include "blockdevice/loopback.h" +#include "blockdevice/sd.h" +#include "filesystem/fat.h" +#include "filesystem/littlefs.h" +#include "filesystem/vfs.h" + +#define COLOR_GREEN(format) ("\e[32m" format "\e[0m") +#define FLASH_START_AT (0.5 * 1024 * 1024) +#define FLASH_LENGTH_ALL 0 +#define LITTLEFS_BLOCK_CYCLE 500 +#define LITTLEFS_LOOKAHEAD_SIZE 16 +#define MIN_FILENO 3 +#define BLOCKDEVICE_HEAP_SIZE (64 * 1024) + +#define PICO_SPI1_TX_PIN 15 +#define PICO_SPI1_RX_PIN 12 +#define PICO_SPI1_SCK_PIN 19 +#define PICO_SPI1_CSN_PIN 17 + +static void test_printf(const char *format, ...) { + va_list args; + va_start(args, format); + int n = vprintf(format, args); + va_end(args); + + printf(" "); + for (size_t i = 0; i < 50 - (size_t)n; i++) + printf("."); +} + +static void setup(blockdevice_t *device) { + (void)device; +} + +static void cleanup(blockdevice_t *device) { + size_t length = device->size(device); + // Deinit is performed when unmounting, so re-init is required. + device->init(device); + device->erase(device, 0, length); +} + +static void test_api_format(filesystem_t *fs, blockdevice_t *device) { + test_printf("fs_format"); + + int err = fs_format(fs, device); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_mount(filesystem_t *fs, blockdevice_t *device) { + test_printf("fs_mount"); + + int err = fs_mount("/", fs, device); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_format_error(filesystem_t *fs, blockdevice_t *device) { + test_printf("fs_format error"); + + int err = fs_format(fs, device); + assert(err == -1); + assert(errno == 5005); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_mount_error(filesystem_t *fs, blockdevice_t *device) { + test_printf("fs_mount error"); + + int err = fs_mount("/", fs, device); + assert(err == -1); + assert(errno == 5005); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_open_close() { + test_printf("open,close"); + + int fd = open("/file", O_RDONLY); // non-existing file + assert(fd == -1); + assert(errno == ENOENT); + + fd = open("/file", O_WRONLY|O_CREAT); + assert(fd >= MIN_FILENO); + + int err = close(fd); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_open_many() { + test_printf("open many files"); + + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd >= MIN_FILENO); + + int err = close(fd); + assert(err == 0); + + int fd1 = open("/file", O_WRONLY|O_CREAT); + assert(fd1 >= MIN_FILENO); + int fd2 = open("/file2", O_WRONLY|O_CREAT); + assert(fd2 == fd1 + 1); + int fd3 = open("/file3", O_WRONLY|O_CREAT); + assert(fd3 == fd1 + 2); + int fd4 = open("/file4", O_WRONLY|O_CREAT); + assert(fd4 == fd1 + 3); + int fd5 = open("/file5", O_WRONLY|O_CREAT); + assert(fd5 == fd1 + 4); + + err = close(fd5); + assert(err == 0); + err = close(fd4); + assert(err == 0); + err = close(fd3); + assert(err == 0); + err = close(fd2); + assert(err == 0); + err = close(fd1); + assert(err == 0); + + int fd6 = open("/file6", O_WRONLY|O_CREAT); + assert(fd6 == fd1); + err = close(fd6); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + + +static void test_api_file_write_read() { + test_printf("write,read"); + + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd != -1); + + char write_buffer[512] = "Hello World!"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + int err = close(fd); + assert(err == 0); + + fd = open("/file", O_RDONLY); + assert(fd != -1); + char read_buffer[512] = {0}; + ssize_t read_length = read(fd, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(read_buffer, write_buffer) == 0); + + err = close(fd); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_seek() { + test_printf("lseek"); + + int fd = open("/file", O_RDWR|O_CREAT); + assert(fd != -1); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t offset = lseek(fd, 0, SEEK_SET); + assert(offset == 0); + + char read_buffer[512] = {0}; + ssize_t read_length = read(fd, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(write_buffer, read_buffer) == 0); + + offset = lseek(fd, 9, SEEK_SET); + assert(offset == 9); + memset(read_buffer, 0, sizeof(read_buffer)); + read_length = read(fd, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer) - 9); + assert(strcmp("ABCDEF", read_buffer) == 0); + + int err = close(fd); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_tell() { + test_printf("ftell"); + + FILE *fp = fopen("/file", "w+"); + assert(fp != NULL); + + char write_data[] = "123456789ABCDEF"; + size_t write_length = fwrite(write_data, 1, strlen(write_data), fp); + assert(write_length == strlen(write_data)); + + int err = fflush(fp); + assert(err == 0); + + long pos = ftell(fp); + assert((size_t)pos == strlen(write_data)); + + err = fseek(fp, 0, SEEK_SET); + assert(err == 0); + + pos = ftell(fp); + assert(pos == 0); + + err = fseek(fp, 0, SEEK_END); + assert(err == 0); + pos = ftell(fp); + assert((size_t)pos == strlen(write_data)); + + err = fclose(fp); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_file_truncate() { + test_printf("ftruncate"); + + int fd = open("/file", O_RDWR|O_CREAT); + assert(fd != -1); + + char write_buffer[] = "123456789ABCDEF"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + off_t offset = lseek(fd, 0, SEEK_SET); + assert(offset == 0); + + int err = ftruncate(fd, 9); + assert(err == 0); + + char read_buffer[512] = {0}; + ssize_t read_length = read(fd, read_buffer, sizeof(read_buffer)); + assert(read_length == 9); + assert(strcmp(read_buffer, "123456789") == 0); + + err = close(fd); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_mkdir() { + test_printf("mkdir,rmdir"); + + int err = mkdir("/dir-create", 0777); + assert(err == 0); + + struct stat finfo; + err = stat("/dir-create", &finfo); + assert(err == 0); + assert(finfo.st_mode & S_IFDIR); + + err = rmdir("/dir-create"); + assert(err == 0); + + err = stat("/dir-create", &finfo); + assert(err == -1 && errno == ENOENT); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_dir_open() { + test_printf("opendir,closedir"); + + DIR *dir = opendir("/dir-non-exists"); // non-exists directory + assert(dir == NULL); + assert((errno == ENOTDIR) || (errno == ENOENT)); // NOTE: FAT returns ENOTDIR, littlefs returns ENOENT + + int err = mkdir("/dir", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + + dir = opendir("/dir"); + assert(dir != NULL); + + err = closedir(dir); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_dir_open_many() { + test_printf("opendir many dir"); + + int err = mkdir("/dir1", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + err = mkdir("/dir2", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + err = mkdir("/dir3", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + err = mkdir("/dir4", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + err = mkdir("/dir5", 0777); + assert((err == 0) || (err -1 && errno == EEXIST)); + + DIR *dir1 = opendir("/dir1"); + assert(dir1 != NULL); + DIR *dir2 = opendir("/dir2"); + assert(dir2 != NULL); + DIR *dir3 = opendir("/dir3"); + assert(dir3 != NULL); + DIR *dir4 = opendir("/dir4"); + assert(dir4 != NULL); + DIR *dir5 = opendir("/dir5"); + assert(dir5 != NULL); + + err = closedir(dir5); + assert(err == 0); + err = closedir(dir4); + assert(err == 0); + err = closedir(dir3); + assert(err == 0); + err = closedir(dir2); + assert(err == 0); + err = closedir(dir1); + assert(err == 0); + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_dir_read() { + test_printf("readdir"); + + int err = mkdir("/dir", 0777); + assert(err == 0 || (err == -1 && errno == EEXIST)); + + // add regular file + int fd = open("/dir/file", O_WRONLY|O_CREAT); + assert(fd != -1); + err = close(fd); + assert(err == 0); + + DIR *dir = opendir("/dir"); + assert(dir != NULL); + + struct dirent *ent = readdir(dir); + assert(ent != NULL); + if (ent->d_type == DT_DIR) { + // for not FAT file system + assert(strcmp(ent->d_name, ".") == 0); + + ent = readdir(dir); + assert(ent != NULL); + assert(ent->d_type == DT_DIR); + assert(strcmp(ent->d_name, "..") == 0); + + ent = readdir(dir); + assert(ent != NULL); + } + assert(ent->d_type == DT_REG); + assert(strcmp(ent->d_name, "file") == 0); + + ent = readdir(dir); // Reach the end of the directory + assert(ent == NULL); + assert(errno == 0); + + err = closedir(dir); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_remove() { + test_printf("unlink"); + + int err = unlink("/not-exists"); + assert(err == -1); + assert(errno == ENOENT); + + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd != -1); + err = close(fd); + assert(err == 0); + + err = unlink("/file"); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_rename() { + test_printf("rename"); + + int err = rename("/not-exists", "/renamed"); + assert(err == -1); + assert(errno == ENOENT); + + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd != -1); + char write_buffer[512] = "Hello World!"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + err = close(fd); + assert(err == 0); + + err = rename("/file", "/renamed"); + assert(err == 0); + + fd = open("/renamed", O_RDONLY); + assert(fd != -1); + char read_buffer[512] = {0}; + ssize_t read_length = read(fd, read_buffer, sizeof(read_buffer)); + assert((size_t)read_length == strlen(write_buffer)); + assert(strcmp(read_buffer, write_buffer) == 0); + + err = close(fd); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_stat() { + test_printf("lstat"); + + // regular file + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd != -1); + char write_buffer[512] = "Hello World!"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + + int err = close(fd); + assert(err == 0); + + struct stat finfo; + err = stat("/file", &finfo); + assert(err == 0); + assert((size_t)finfo.st_size == strlen(write_buffer)); + assert(finfo.st_mode & S_IFREG); + + // directory + err = mkdir("/dir", 0777); + assert(err == 0 || (err == -1 && errno == EEXIST)); + err = stat("/dir", &finfo); + assert(err == 0); + assert(finfo.st_mode & S_IFDIR); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_reformat(void) { + test_printf("fs_reformat"); + + int fd = open("/file", O_WRONLY|O_CREAT); + assert(fd != -1); + char write_buffer[512] = "Hello World!"; + ssize_t write_length = write(fd, write_buffer, strlen(write_buffer)); + assert((size_t)write_length == strlen(write_buffer)); + int err = close(fd); + assert(err == 0); + + err = fs_reformat("/"); + assert(err == 0); + + struct stat finfo; + err = stat("/file", &finfo); + assert(err == -1); + assert(errno == ENOENT); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_unmount(void) { + test_printf("fs_unmount"); + + int err = fs_unmount("/"); + assert(err == 0); + + printf(COLOR_GREEN("ok\n")); +} + +static void test_api_mount_unmount_repeat(filesystem_t *fs, blockdevice_t *device) { + test_printf("fs_mount,fs_unmount repeat"); + + for (size_t i = 0; i < 20; i++) { + int err = fs_mount("/", fs, device); + assert(err == 0); + err = fs_unmount("/"); + assert(err == 0); + } + + printf(COLOR_GREEN("ok\n")); +} + +static void test_loopback_file(void) { + test_printf("loopback image file"); + + struct stat finfo; + int err = stat("/flash/loopback.dmg", &finfo); + assert(err == 0); + assert(finfo.st_mode & S_IFREG); + assert((size_t)finfo.st_size > 0); + + printf(COLOR_GREEN("ok\n")); +} + +void test_vfs(void) { + printf("VFS FAT:\n"); + blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + assert(flash != NULL); + filesystem_t *fat = filesystem_fat_create(); + assert(fat != NULL); + setup(flash); + + test_api_format(fat, flash); + test_api_mount(fat, flash); + test_api_file_open_close(); + test_api_file_open_many(); + test_api_file_write_read(); + test_api_file_seek(); + test_api_file_tell(); + test_api_file_truncate(); + test_api_stat(); + test_api_remove(); + test_api_rename(); + test_api_mkdir(); + test_api_dir_open(); + test_api_dir_open_many(); + test_api_dir_read(); + test_api_reformat(); + test_api_unmount(); + test_api_mount_unmount_repeat(fat, flash); + + cleanup(flash); + blockdevice_flash_free(flash); + filesystem_fat_free(fat); + + printf("VFS littlefs:\n"); + flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + assert(flash != NULL); + filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, + LITTLEFS_LOOKAHEAD_SIZE); + assert(lfs != NULL); + setup(flash); + + test_api_format(lfs, flash); + test_api_mount(lfs, flash); + test_api_file_open_close(); + test_api_file_open_many(); + test_api_file_write_read(); + test_api_file_seek(); + test_api_file_tell(); + test_api_file_truncate(); + test_api_stat(); + test_api_remove(); + test_api_rename(); + test_api_mkdir(); + test_api_dir_open(); + test_api_dir_open_many(); + test_api_dir_read(); + test_api_reformat(); + test_api_unmount(); + test_api_mount_unmount_repeat(lfs, flash); + + cleanup(flash); + blockdevice_flash_free(flash); + filesystem_littlefs_free(lfs); + + printf("VFS littlefs on the Heap:\n"); + blockdevice_t *heap = blockdevice_heap_create(BLOCKDEVICE_HEAP_SIZE); + assert(heap != NULL); + lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, + LITTLEFS_LOOKAHEAD_SIZE); + assert(lfs != NULL); + setup(heap); + + test_api_format(lfs, heap); + test_api_mount(lfs, heap); + test_api_file_open_close(); + test_api_file_open_many(); + test_api_file_write_read(); + test_api_file_seek(); + test_api_file_tell(); + test_api_file_truncate(); + test_api_stat(); + test_api_remove(); + test_api_rename(); + test_api_mkdir(); + test_api_dir_open(); + test_api_dir_open_many(); + test_api_dir_read(); + test_api_reformat(); + test_api_unmount(); + test_api_mount_unmount_repeat(lfs, heap); + + cleanup(heap); + filesystem_littlefs_free(lfs); + blockdevice_heap_free(heap); + + + printf("VFS loopback FAT on littlefs:\n"); + flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + assert(flash != NULL); + lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, + LITTLEFS_LOOKAHEAD_SIZE); + assert(lfs != NULL); + setup(flash); + int err = fs_format(lfs, flash); + assert(err == 0); + err = fs_mount("/flash", lfs, flash); + assert(err == 0); + + blockdevice_t *loopback = blockdevice_loopback_create("/flash/loopback.dmg", 64 * 1024, 512); + fat = filesystem_fat_create(); + assert(loopback != NULL); + assert(fat != NULL); + + test_api_format(fat, loopback); + test_api_mount(fat, loopback); + test_api_file_open_close(); + test_api_file_open_many(); + test_api_file_write_read(); + test_api_file_seek(); + test_api_file_tell(); + test_api_file_truncate(); + test_api_stat(); + test_api_remove(); + test_api_rename(); + test_api_mkdir(); + test_api_dir_open(); + test_api_dir_open_many(); + test_api_dir_read(); + test_api_reformat(); + test_api_mount_unmount_repeat(fat, loopback); + + filesystem_fat_free(fat); + blockdevice_loopback_free(loopback); + + test_loopback_file(); + + fs_unmount("/flash"); + cleanup(flash); + blockdevice_flash_free(flash); + filesystem_littlefs_free(lfs); + + printf("VFS not connected SD card error handling:\n"); + blockdevice_t *sd = blockdevice_sd_create(spi1, // SPI1 without connection + PICO_SPI1_TX_PIN, + PICO_SPI1_RX_PIN, + PICO_SPI1_SCK_PIN, + PICO_SPI1_CSN_PIN, + 10 * MHZ, + false); + assert(sd != NULL); + fat = filesystem_fat_create(); + assert(fat != NULL); + setup(sd); + + test_api_format_error(fat, sd); + test_api_mount_error(fat, sd); + + filesystem_fat_free(fat); + blockdevice_sd_free(sd); +} diff --git a/tests/multicore/CMakeLists.txt b/tests/multicore/CMakeLists.txt new file mode 100644 index 0000000..0e5db57 --- /dev/null +++ b/tests/multicore/CMakeLists.txt @@ -0,0 +1,26 @@ +add_executable(multicore main.c) +target_compile_options(multicore PRIVATE -Werror -Wall -Wextra -Wnull-dereference) +target_link_libraries(multicore PRIVATE + pico_stdlib + pico_multicore + blockdevice_flash + blockdevice_heap + blockdevice_sd + filesystem_fat + filesystem_littlefs + filesystem_vfs +) +target_link_options(multicore PRIVATE -Wl,--print-memory-usage) +pico_add_extra_outputs(multicore) +pico_enable_stdio_uart(multicore 1) +pico_enable_stdio_usb(multicore 0) +pico_set_binary_type(multicore copy_to_ram) + + +find_program(OPENOCD openocd) +if(OPENOCD) + add_custom_target(run_multicore + COMMAND ${OPENOCD} -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program multicore.elf verify reset exit" + DEPENDS multicore + ) +endif() diff --git a/tests/multicore.c b/tests/multicore/main.c similarity index 100% rename from tests/multicore.c rename to tests/multicore/main.c diff --git a/tests/test_blockdevice.c b/tests/test_blockdevice.c index 1beca45..aa8e9bc 100644 --- a/tests/test_blockdevice.c +++ b/tests/test_blockdevice.c @@ -3,17 +3,13 @@ #include #include #include -#include "blockdevice/flash.h" #include "blockdevice/heap.h" #include "blockdevice/loopback.h" -#include "blockdevice/sd.h" #include "filesystem/fat.h" #include "filesystem/vfs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") -#define FLASH_START_AT (0.5 * 1024 * 1024) -#define FLASH_LENGTH_ALL 0 -#define HEAP_STORAGE_SIZE (512 * 128) // 64KB +#define HEAP_STORAGE_SIZE (64 * 1024) #define LOOPBACK_STORAGE_SIZE 1024 #define LOOPBACK_BLOCK_SIZE 512 @@ -59,20 +55,6 @@ static void cleanup(blockdevice_t *device) { device->erase(device, 0, length); } -static bool is_sd_card_connected(blockdevice_t *device) { - int err = device->deinit(device); - assert(err == BD_ERROR_OK); - - err = device->init(device); - if (err == BD_ERROR_OK) - return true; - else if (err == -5005) - return false; - else - assert(err == BD_ERROR_OK); - return false; -} - static void test_api_init(blockdevice_t *device) { test_printf("init"); @@ -158,47 +140,6 @@ static void test_api_attribute(blockdevice_t *device) { } void test_blockdevice(void) { - printf("Block device Onboard-Flash:\n"); - blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); - assert(flash != NULL); - setup(flash); - - test_api_init(flash); - test_api_erase_program_read(flash); - test_api_trim(flash); - test_api_sync(flash); - test_api_size(flash); - test_api_attribute(flash); - - cleanup(flash); - blockdevice_flash_free(flash); - - printf("Block device SPI SD card:\n"); - blockdevice_t *sd = blockdevice_sd_create(spi0, - PICO_DEFAULT_SPI_TX_PIN, - PICO_DEFAULT_SPI_RX_PIN, - PICO_DEFAULT_SPI_SCK_PIN, - PICO_DEFAULT_SPI_CSN_PIN, - 10 * MHZ, - false); - assert(sd != NULL); - if (is_sd_card_connected(sd)) { - setup(sd); - - test_api_init(sd); - test_api_erase_program_read(sd); - test_api_trim(sd); - test_api_sync(sd); - test_api_size(sd); - test_api_attribute(sd); - - cleanup(sd); - } else { - test_printf("init"); - printf("skip, device not connected\n"); - } - blockdevice_sd_free(sd); - printf("Block device Heap memory:\n"); blockdevice_t *heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); assert(heap != NULL); diff --git a/tests/test_copy_between_different_filesystems.c b/tests/test_copy_between_different_filesystems.c index abb7e17..30ab3be 100644 --- a/tests/test_copy_between_different_filesystems.c +++ b/tests/test_copy_between_different_filesystems.c @@ -4,14 +4,14 @@ #include #include #include -#include "blockdevice/flash.h" -#include "blockdevice/sd.h" +#include "blockdevice/heap.h" #include "filesystem/littlefs.h" #include "filesystem/fat.h" #include "filesystem/vfs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") +#define HEAP_STORAGE_SIZE (64 * 1024) struct combination_map { blockdevice_t *device1; @@ -20,46 +20,30 @@ struct combination_map { filesystem_t *filesystem2; }; -#define NUM_COMBINATION 6 +#define NUM_COMBINATION 3 static struct combination_map combination[NUM_COMBINATION]; static void init_filesystem_combination(void) { - blockdevice_t *flash1 = blockdevice_flash_create(1 * 1024 * 1024, 512 * 1024); - blockdevice_t *flash2 = blockdevice_flash_create(1 * 1024 * 1024 + 512 * 1024, 0); - blockdevice_t *sd = blockdevice_sd_create(spi0, - PICO_DEFAULT_SPI_TX_PIN, - PICO_DEFAULT_SPI_RX_PIN, - PICO_DEFAULT_SPI_SCK_PIN, - PICO_DEFAULT_SPI_CSN_PIN, - 24 * MHZ, - true); + blockdevice_t *heap1 = blockdevice_heap_create(HEAP_STORAGE_SIZE); + blockdevice_t *heap2 = blockdevice_heap_create(HEAP_STORAGE_SIZE); filesystem_t *fat1 = filesystem_fat_create(); filesystem_t *fat2 = filesystem_fat_create(); filesystem_t *littlefs1 = filesystem_littlefs_create(500, 16); filesystem_t *littlefs2 = filesystem_littlefs_create(500, 16); combination[0] = (struct combination_map){ - .device1 = flash1, .filesystem1 = fat1, .device2 = flash2, .filesystem2 = fat2, + .device1 = heap1, .filesystem1 = fat1, .device2 = heap2, .filesystem2 = fat2, }; combination[1] = (struct combination_map){ - .device1 = flash1, .filesystem1 = fat1, .device2 = flash2, .filesystem2 = littlefs2, + .device1 = heap1, .filesystem1 = fat1, .device2 = heap2, .filesystem2 = littlefs2, }; combination[2] = (struct combination_map){ - .device1 = flash1, .filesystem1 = littlefs1, .device2 = flash2, .filesystem2 = littlefs2, - }; - combination[3] = (struct combination_map){ - .device1 = flash1, .filesystem1 = fat1, .device2 = sd, .filesystem2 = fat2, - }; - combination[4] = (struct combination_map){ - .device1 = flash1, .filesystem1 = fat1, .device2 = sd, .filesystem2 = littlefs2, - }; - combination[5] = (struct combination_map){ - .device1 = flash1, .filesystem1 = littlefs1, .device2 = sd, .filesystem2 = littlefs2, + .device1 = heap1, .filesystem1 = littlefs1, .device2 = heap2, .filesystem2 = littlefs2, }; } -#define TEST_FILE_SIZE 100 * 1024; +#define TEST_FILE_SIZE 16 * 1024; static void test_printf(const char *format, ...) { va_list args; @@ -77,7 +61,7 @@ static void test_write(const char *path) { int fd = open(path, O_WRONLY|O_CREAT); assert(fd >= 0); - uint8_t buffer[1024*64] = {0}; + uint8_t buffer[512] = {0}; size_t remind = TEST_FILE_SIZE; while (remind > 0) { size_t chunk = remind % sizeof(buffer) ? remind % sizeof(buffer) : sizeof(buffer); @@ -99,7 +83,7 @@ static void test_copy(const char *source, const char *dist) { assert(fd_dist >= 0); assert(fd_src != fd_dist); - uint8_t buffer[1024*64] = {0}; + uint8_t buffer[512] = {0}; while (true) { ssize_t read_size = read(fd_src, buffer, sizeof(buffer)); if (read_size == 0) @@ -119,7 +103,7 @@ static void test_read(const char *path) { int fd = open(path, O_RDONLY); assert(fd >= 0); - uint8_t buffer[1024*64] = {0}; + uint8_t buffer[512] = {0}; while (true) { ssize_t read_size = read(fd, buffer, sizeof(buffer)); if (read_size == 0) diff --git a/tests/test_filesystem.c b/tests/test_filesystem.c index dd8f2bc..8fef599 100644 --- a/tests/test_filesystem.c +++ b/tests/test_filesystem.c @@ -2,13 +2,12 @@ #include #include #include -#include "blockdevice/flash.h" +#include "blockdevice/heap.h" #include "filesystem/fat.h" #include "filesystem/littlefs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") -#define FLASH_START_AT (0.5 * 1024 * 1024) -#define FLASH_LENGTH_ALL 0 +#define HEAP_STORAGE_SIZE (128 * 1024) #define LITTLEFS_BLOCK_CYCLE 500 #define LITTLEFS_LOOKAHEAD_SIZE 16 @@ -359,15 +358,15 @@ static void test_api_stat(filesystem_t *fs) { void test_filesystem(void) { printf("File system FAT:\n"); - blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + blockdevice_t *heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); - assert(flash != NULL); + assert(heap != NULL); filesystem_t *fat = filesystem_fat_create(); assert(fat != NULL); - setup(flash); + setup(heap); - test_api_format(fat, flash); - test_api_mount(fat, flash); + test_api_format(fat, heap); + test_api_mount(fat, heap); test_api_file_open_close(fat); test_api_file_write_read(fat); test_api_file_seek(fat); @@ -381,21 +380,21 @@ void test_filesystem(void) { test_api_stat(fat); test_api_unmount(fat); - cleanup(flash); - blockdevice_flash_free(flash); + cleanup(heap); filesystem_fat_free(fat); + blockdevice_heap_free(heap); printf("File system littlefs:\n"); - flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); - assert(flash != NULL); + heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, LITTLEFS_LOOKAHEAD_SIZE); assert(lfs != NULL); - setup(flash); + setup(heap); - test_api_format(lfs, flash); - test_api_mount(lfs, flash); + test_api_format(lfs, heap); + test_api_mount(lfs, heap); test_api_file_open_close(lfs); test_api_file_write_read(lfs); test_api_file_seek(lfs); @@ -410,7 +409,7 @@ void test_filesystem(void) { test_api_unmount(lfs); - cleanup(flash); - blockdevice_flash_free(flash); + cleanup(heap); filesystem_littlefs_free(lfs); + blockdevice_heap_free(heap); } diff --git a/tests/test_stdio.c b/tests/test_stdio.c index 84f9b32..60165fe 100644 --- a/tests/test_stdio.c +++ b/tests/test_stdio.c @@ -4,14 +4,13 @@ #include #include #include -#include "blockdevice/flash.h" +#include "blockdevice/heap.h" #include "filesystem/fat.h" #include "filesystem/littlefs.h" #include "filesystem/vfs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") -#define FLASH_START_AT (0.5 * 1024 * 1024) -#define FLASH_LENGTH_ALL 0 +#define HEAP_STORAGE_SIZE (128 * 1024) #define LITTLEFS_BLOCK_CYCLE 500 #define LITTLEFS_LOOKAHEAD_SIZE 16 @@ -981,28 +980,28 @@ void test_standard_file_api(void) { void test_stdio(void) { printf("POSIX and C standard file API(littlefs):\n"); - blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + blockdevice_t *heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, LITTLEFS_LOOKAHEAD_SIZE); - setup(lfs, flash); + setup(lfs, heap); test_standard_file_api(); - cleanup(lfs, flash); + cleanup(lfs, heap); filesystem_littlefs_free(lfs); - blockdevice_flash_free(flash); + blockdevice_heap_free(heap); printf("POSIX and C standard file API(FAT):\n"); - flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); + heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); filesystem_t *fat = filesystem_fat_create(); - setup(fat, flash); + setup(fat, heap); test_standard_file_api(); - cleanup(fat, flash); + cleanup(fat, heap); filesystem_fat_free(fat); - blockdevice_flash_free(flash); + blockdevice_heap_free(heap); } diff --git a/tests/test_vfs.c b/tests/test_vfs.c index 54eece2..a6b2ce2 100644 --- a/tests/test_vfs.c +++ b/tests/test_vfs.c @@ -6,18 +6,14 @@ #include #include #include -#include -#include "blockdevice/flash.h" #include "blockdevice/heap.h" #include "blockdevice/loopback.h" -#include "blockdevice/sd.h" #include "filesystem/fat.h" #include "filesystem/littlefs.h" #include "filesystem/vfs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") -#define FLASH_START_AT (0.5 * 1024 * 1024) -#define FLASH_LENGTH_ALL 0 +#define HEAP_STORAGE_SIZE (128 * 1024) #define LITTLEFS_BLOCK_CYCLE 500 #define LITTLEFS_LOOKAHEAD_SIZE 16 #define MIN_FILENO 3 @@ -507,7 +503,7 @@ static void test_loopback_file(void) { test_printf("loopback image file"); struct stat finfo; - int err = stat("/flash/loopback.dmg", &finfo); + int err = stat("/heap/loopback.dmg", &finfo); assert(err == 0); assert(finfo.st_mode & S_IFREG); assert((size_t)finfo.st_size > 0); @@ -517,14 +513,14 @@ static void test_loopback_file(void) { void test_vfs(void) { printf("VFS FAT:\n"); - blockdevice_t *flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); - assert(flash != NULL); + blockdevice_t *heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); filesystem_t *fat = filesystem_fat_create(); assert(fat != NULL); - setup(flash); + setup(heap); - test_api_format(fat, flash); - test_api_mount(fat, flash); + test_api_format(fat, heap); + test_api_mount(fat, heap); test_api_file_open_close(); test_api_file_open_many(); test_api_file_write_read(); @@ -540,22 +536,22 @@ void test_vfs(void) { test_api_dir_read(); test_api_reformat(); test_api_unmount(); - test_api_mount_unmount_repeat(fat, flash); + test_api_mount_unmount_repeat(fat, heap); - cleanup(flash); - blockdevice_flash_free(flash); + cleanup(heap); filesystem_fat_free(fat); + blockdevice_heap_free(heap); printf("VFS littlefs:\n"); - flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); - assert(flash != NULL); + heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); filesystem_t *lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, LITTLEFS_LOOKAHEAD_SIZE); assert(lfs != NULL); - setup(flash); + setup(heap); - test_api_format(lfs, flash); - test_api_mount(lfs, flash); + test_api_format(lfs, heap); + test_api_mount(lfs, heap); test_api_file_open_close(); test_api_file_open_many(); test_api_file_write_read(); @@ -571,14 +567,14 @@ void test_vfs(void) { test_api_dir_read(); test_api_reformat(); test_api_unmount(); - test_api_mount_unmount_repeat(lfs, flash); + test_api_mount_unmount_repeat(lfs, heap); - cleanup(flash); - blockdevice_flash_free(flash); + cleanup(heap); + blockdevice_heap_free(heap); filesystem_littlefs_free(lfs); printf("VFS littlefs on the Heap:\n"); - blockdevice_t *heap = blockdevice_heap_create(BLOCKDEVICE_HEAP_SIZE); + heap = blockdevice_heap_create(BLOCKDEVICE_HEAP_SIZE); assert(heap != NULL); lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, LITTLEFS_LOOKAHEAD_SIZE); @@ -610,18 +606,18 @@ void test_vfs(void) { printf("VFS loopback FAT on littlefs:\n"); - flash = blockdevice_flash_create(FLASH_START_AT, FLASH_LENGTH_ALL); - assert(flash != NULL); + heap = blockdevice_heap_create(HEAP_STORAGE_SIZE); + assert(heap != NULL); lfs = filesystem_littlefs_create(LITTLEFS_BLOCK_CYCLE, LITTLEFS_LOOKAHEAD_SIZE); assert(lfs != NULL); - setup(flash); - int err = fs_format(lfs, flash); + setup(heap); + int err = fs_format(lfs, heap); assert(err == 0); - err = fs_mount("/flash", lfs, flash); + err = fs_mount("/heap", lfs, heap); assert(err == 0); - blockdevice_t *loopback = blockdevice_loopback_create("/flash/loopback.dmg", 64 * 1024, 512); + blockdevice_t *loopback = blockdevice_loopback_create("/heap/loopback.dmg", 64 * 1024, 512); fat = filesystem_fat_create(); assert(loopback != NULL); assert(fat != NULL); @@ -649,27 +645,8 @@ void test_vfs(void) { test_loopback_file(); - fs_unmount("/flash"); - cleanup(flash); - blockdevice_flash_free(flash); + fs_unmount("/heap"); + cleanup(heap); filesystem_littlefs_free(lfs); - - printf("VFS not connected SD card error handling:\n"); - blockdevice_t *sd = blockdevice_sd_create(spi1, // SPI1 without connection - PICO_SPI1_TX_PIN, - PICO_SPI1_RX_PIN, - PICO_SPI1_SCK_PIN, - PICO_SPI1_CSN_PIN, - 10 * MHZ, - false); - assert(sd != NULL); - fat = filesystem_fat_create(); - assert(fat != NULL); - setup(sd); - - test_api_format_error(fat, sd); - test_api_mount_error(fat, sd); - - filesystem_fat_free(fat); - blockdevice_sd_free(sd); + blockdevice_heap_free(heap); }