fork the esp-idf fatfs for f_forward and exfat support

This commit is contained in:
jacqueline
2023-07-25 17:42:00 +10:00
parent 9287c4eb8c
commit 7b72e5479e
86 changed files with 34272 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# fatfs component target tests
This directory contains tests for `fatfs` component which are run on chip targets.
See also [test_fatfs_host](../test_fatfs_host) directory for the tests which run on a Linux host.
Fatfs tests can be executed with different `diskio` backends: `diskio_sdmmc` (SD cards over SD or SPI interface), `diskio_spiflash` (wear levelling in internal flash) and `diskio_rawflash` (read-only, no wear levelling, internal flash). There is one test app here for each of these backends:
- [sdcard](sdcard/) — runs fatfs tests with an SD card over SDMMC or SDSPI interface
- [flash_wl](flash_wl/) - runs fatfs test in a wear_levelling partition in SPI flash
- [flash_ro](flash_ro/) - runs fatfs test in a read-only (no wear levelling) partition in SPI flash
These test apps define:
- test functions
- setup/teardown routines
- build/test configurations
- pytest test runners
The actual test cases (many of which are common between the test apps) are defined in the [test_fatfs_common component](test_fatfs_common/).
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../test_fatfs_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_fatfs_flash_ro)
+14
View File
@@ -0,0 +1,14 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
This test app runs a few FATFS test cases in a read-only FAT partition.
These tests should be possible to run on any ESP development board, not extra hardware is necessary.
The initial FAT image is generated during the build process in [main/CMakeLists.txt](main/CMakeLists.txt):
- `create_test_files` function creates a set of files expected by the test cases
- `fatfs_create_rawflash_image` generates a FAT image from the set of files (via `fatfsgen.py`)
The generated FAT image is flashed into `storage` partition when running `idf.py flash`.
See [../README.md](../README.md) for more information about FATFS test apps.
@@ -0,0 +1,41 @@
idf_component_register(SRCS "test_fatfs_flash_ro.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity spi_flash fatfs vfs test_fatfs_common
WHOLE_ARCHIVE)
set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/fatfs_image")
# This helper function creates a set of files expected by the test case.
# The files are then added into the FAT image by 'fatfs_create_rawflash_image' below.
function(create_test_files)
message(STATUS "Generating source files for test_fatfs_flash_ro in ${out_dir}...")
# used in "(raw) can read file"
file(WRITE "${out_dir}/hello.txt" "Hello, World!\n")
# used in "(raw) can open maximum number of files"
foreach(i RANGE 1 32)
file(WRITE "${out_dir}/f/${i}.txt")
endforeach()
# used in "(raw) opendir, readdir, rewinddir, seekdir work as expected"
file(WRITE "${out_dir}/dir/1.txt")
file(WRITE "${out_dir}/dir/2.txt")
file(WRITE "${out_dir}/dir/boo.bin")
file(WRITE "${out_dir}/dir/inner/3.txt")
# used in "(raw) multiple tasks can use same volume"
foreach(i RANGE 1 4)
string(REPEAT "${i}" 32000 file_content)
file(WRITE "${out_dir}/ccrnt/${i}.txt" ${file_content})
endforeach()
# used in "(raw) read speed test"
string(REPEAT "a" 262144 file_content)
file(WRITE "${out_dir}/256k.bin" ${file_content})
endfunction()
create_test_files()
fatfs_create_rawflash_image(storage ${out_dir} FLASH_IN_PROJECT PRESERVE_TIME)
@@ -0,0 +1,311 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include "unity.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "test_fatfs_common.h"
void app_main(void)
{
unity_run_menu();
}
static void test_setup(size_t max_files)
{
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = max_files
};
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_ro("/spiflash", "storage", &mount_config));
}
static void test_teardown(void)
{
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_ro("/spiflash", "storage"));
}
TEST_CASE("(raw) can read file", "[fatfs]")
{
test_setup(5);
FILE* f = fopen("/spiflash/hello.txt", "r");
TEST_ASSERT_NOT_NULL(f);
char buf[32] = { 0 };
int cb = fread(buf, 1, sizeof(buf), f);
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), cb);
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
TEST_ASSERT_EQUAL(0, fclose(f));
test_teardown();
}
TEST_CASE("(raw) can open maximum number of files", "[fatfs]")
{
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
test_setup(max_files);
FILE** files = calloc(max_files, sizeof(FILE*));
for (size_t i = 0; i < max_files; ++i) {
char name[32];
snprintf(name, sizeof(name), "/spiflash/f/%d.txt", i + 1);
files[i] = fopen(name, "r");
TEST_ASSERT_NOT_NULL(files[i]);
}
/* close everything and clean up */
for (size_t i = 0; i < max_files; ++i) {
fclose(files[i]);
}
free(files);
test_teardown();
}
TEST_CASE("(raw) can lseek", "[fatfs]")
{
test_setup(5);
FILE* f = fopen("/spiflash/hello.txt", "r");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(0, fseek(f, 2, SEEK_CUR));
TEST_ASSERT_EQUAL('l', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, 4, SEEK_SET));
TEST_ASSERT_EQUAL('o', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, -5, SEEK_END));
TEST_ASSERT_EQUAL('r', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END));
TEST_ASSERT_EQUAL(17, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
TEST_ASSERT_EQUAL(14, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
test_teardown();
}
TEST_CASE("(raw) stat returns correct values", "[fatfs]")
{
test_setup(5);
struct tm tm;
tm.tm_year = 2018 - 1900;
tm.tm_mon = 5; // Note: month can be 0-11 & not 1-12
tm.tm_mday = 13;
tm.tm_hour = 11;
tm.tm_min = 2;
tm.tm_sec = 10;
time_t t = mktime(&tm);
printf("Reference time: %s", asctime(&tm));
struct stat st;
TEST_ASSERT_EQUAL(0, stat("/spiflash/hello.txt", &st));
time_t mtime = st.st_mtime;
struct tm mtm;
localtime_r(&mtime, &mtm);
printf("File time: %s", asctime(&mtm));
TEST_ASSERT(mtime > t); // Modification time should be in future wrt ref time
TEST_ASSERT(st.st_mode & S_IFREG);
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
memset(&st, 0, sizeof(st));
TEST_ASSERT_EQUAL(0, stat("/spiflash", &st));
TEST_ASSERT(st.st_mode & S_IFDIR);
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
test_teardown();
}
TEST_CASE("(raw) can opendir root directory of FS", "[fatfs]")
{
test_setup(5);
DIR* dir = opendir("/spiflash");
TEST_ASSERT_NOT_NULL(dir);
bool found = false;
while (true) {
struct dirent* de = readdir(dir);
if (!de) {
break;
}
if (strcasecmp(de->d_name, "hello.txt") == 0) {
found = true;
break;
}
}
TEST_ASSERT_TRUE(found);
TEST_ASSERT_EQUAL(0, closedir(dir));
test_teardown();
}
TEST_CASE("(raw) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]")
{
test_setup(5);
DIR* dir = opendir("/spiflash/dir");
TEST_ASSERT_NOT_NULL(dir);
int count = 0;
const char* names[4];
while(count < 4) {
struct dirent* de = readdir(dir);
if (!de) {
break;
}
printf("found '%s'\n", de->d_name);
if (strcasecmp(de->d_name, "1.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "1.txt";
++count;
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "2.txt";
++count;
} else if (strcasecmp(de->d_name, "inner") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
names[count] = "inner";
++count;
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "boo.bin";
++count;
} else {
TEST_FAIL_MESSAGE("unexpected directory entry");
}
}
TEST_ASSERT_EQUAL(count, 4);
rewinddir(dir);
struct dirent* de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
seekdir(dir, 3);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
seekdir(dir, 1);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
seekdir(dir, 2);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
TEST_ASSERT_EQUAL(0, closedir(dir));
test_teardown();
}
typedef struct {
const char* filename;
size_t word_count;
unsigned val;
SemaphoreHandle_t done;
esp_err_t result;
} read_test_arg_t;
#define READ_TEST_ARG_INIT(name, val_) \
{ \
.filename = name, \
.word_count = 8000, \
.val = val_, \
.done = xSemaphoreCreateBinary() \
}
static void read_task(void* param)
{
read_test_arg_t* args = (read_test_arg_t*) param;
FILE* f = fopen(args->filename, "rb");
if (f == NULL) {
args->result = ESP_ERR_NOT_FOUND;
goto done;
}
for (size_t i = 0; i < args->word_count; ++i) {
unsigned rval;
int cnt = fread(&rval, sizeof(rval), 1, f);
if (cnt != 1 || rval != args->val) {
printf("E(r): i=%d, cnt=%d rval=0x08%x val=0x%08x\n", i, cnt, rval, args->val);
args->result = ESP_FAIL;
goto close;
}
}
args->result = ESP_OK;
close:
fclose(f);
done:
xSemaphoreGive(args->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
TEST_CASE("(raw) multiple tasks can use same volume", "[fatfs]")
{
test_setup(5);
char names[4][64];
for (size_t i = 0; i < 4; ++i) {
snprintf(names[i], sizeof(names[i]), "/spiflash/ccrnt/%d.txt", i + 1);
}
read_test_arg_t args1 = READ_TEST_ARG_INIT(names[0], 0x31313131);
read_test_arg_t args2 = READ_TEST_ARG_INIT(names[1], 0x32323232);
read_test_arg_t args3 = READ_TEST_ARG_INIT(names[2], 0x33333333);
read_test_arg_t args4 = READ_TEST_ARG_INIT(names[3], 0x34343434);
const int cpuid_0 = 0;
const int cpuid_1 = portNUM_PROCESSORS - 1;
const int stack_size = 4096;
printf("reading files 1.txt 2.txt 3.txt 4.txt \n");
xTaskCreatePinnedToCore(&read_task, "r1", stack_size, &args1, 3, NULL, cpuid_1);
xTaskCreatePinnedToCore(&read_task, "r2", stack_size, &args2, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_task, "r3", stack_size, &args3, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_task, "r4", stack_size, &args4, 3, NULL, cpuid_1);
xSemaphoreTake(args1.done, portMAX_DELAY);
printf("1.txt done\n");
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
xSemaphoreTake(args2.done, portMAX_DELAY);
printf("2.txt done\n");
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
xSemaphoreTake(args3.done, portMAX_DELAY);
printf("3.txt done\n");
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
xSemaphoreTake(args4.done, portMAX_DELAY);
printf("4.txt done\n");
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
vSemaphoreDelete(args1.done);
vSemaphoreDelete(args2.done);
vSemaphoreDelete(args3.done);
vSemaphoreDelete(args4.done);
test_teardown();
}
TEST_CASE("(raw) read speed test", "[fatfs][timeout=60]")
{
test_setup(5);
const size_t buf_size = 16 * 1024;
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
const size_t file_size = 256 * 1024;
const char* file = "/spiflash/256k.bin";
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false);
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false);
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false);
free(buf);
test_teardown();
}
@@ -0,0 +1,3 @@
# Name, Type, SubType, Offset, Size, Flags
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 528k,
1 # Name Type SubType Offset Size Flags
2 factory app factory 0x10000 1M
3 storage data fat 528k
@@ -0,0 +1,15 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_fatfs_flash_ro(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('*')
dut.expect_unity_test_output()
@@ -0,0 +1,14 @@
# General options for additional checks
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
# disable task watchdog since this app uses an interactive menu
CONFIG_ESP_TASK_WDT_INIT=n
# use custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../test_fatfs_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_fatfs_flash_wl)
+8
View File
@@ -0,0 +1,8 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
This test app runs a few FATFS test cases in a wear levelling FAT partition.
These tests should be possible to run on any ESP development board, not extra hardware is necessary.
See [../README.md](../README.md) for more information about FATFS test apps.
@@ -0,0 +1,4 @@
idf_component_register(SRCS "test_fatfs_flash_wl.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity spi_flash fatfs vfs test_fatfs_common
WHOLE_ARCHIVE)
@@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include "unity.h"
#include "esp_partition.h"
#include "esp_log.h"
#include "esp_random.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "test_fatfs_common.h"
#include "wear_levelling.h"
#include "esp_partition.h"
#include "esp_memory_utils.h"
void app_main(void)
{
unity_run_menu();
}
static wl_handle_t s_test_wl_handle;
static void test_setup(void)
{
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
};
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &mount_config, &s_test_wl_handle));
}
static void test_teardown(void)
{
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", s_test_wl_handle));
}
TEST_CASE("(WL) can format partition", "[fatfs][wear_levelling]")
{
TEST_ESP_OK(esp_vfs_fat_spiflash_format_rw_wl("/spiflash", NULL));
test_setup();
test_teardown();
}
TEST_CASE("(WL) can format when the FAT is mounted already", "[fatfs][wear_levelling]")
{
test_setup();
TEST_ESP_OK(esp_vfs_fat_spiflash_format_rw_wl("/spiflash", NULL));
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
test_fatfs_pread_file("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) can create and write file", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
test_teardown();
}
TEST_CASE("(WL) can create and open file with O_CREAT flag", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_create_file_with_o_creat_flag("/spiflash/hello.txt");
test_fatfs_open_file_with_o_creat_flag("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) can read file", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
test_fatfs_read_file("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) can read file with pread", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
test_fatfs_pread_file("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) pwrite() works well", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_pwrite_file("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) can open maximum number of files", "[fatfs][wear_levelling]")
{
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = max_files
};
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &mount_config, &s_test_wl_handle));
test_fatfs_open_max_files("/spiflash/f", max_files);
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", s_test_wl_handle));
}
TEST_CASE("(WL) overwrite and append file", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_overwrite_append("/spiflash/hello.txt");
test_teardown();
}
TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_lseek("/spiflash/seek.txt");
test_teardown();
}
TEST_CASE("(WL) can truncate", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_truncate_file("/spiflash/truncate.txt");
test_teardown();
}
TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_stat("/spiflash/stat.txt", "/spiflash");
test_teardown();
}
TEST_CASE("(WL) stat returns correct mtime if DST is enabled", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_mtime_dst("/spiflash/statdst.txt", "/spiflash");
test_teardown();
}
TEST_CASE("(WL) utime sets modification time", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_utime("/spiflash/utime.txt", "/spiflash");
test_teardown();
}
TEST_CASE("(WL) unlink removes a file", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_unlink("/spiflash/unlink.txt");
test_teardown();
}
TEST_CASE("(WL) link copies a file, rename moves a file", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_link_rename("/spiflash/link");
test_teardown();
}
TEST_CASE("(WL) can create and remove directories", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_mkdir_rmdir("/spiflash/dir");
test_teardown();
}
TEST_CASE("(WL) can opendir root directory of FS", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_can_opendir("/spiflash");
test_teardown();
}
TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_opendir_readdir_rewinddir("/spiflash/dir");
test_teardown();
}
TEST_CASE("(WL) multiple tasks can use same volume", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_concurrent("/spiflash/f");
test_teardown();
}
TEST_CASE("(WL) fatfs does not ignore leading spaces", "[fatfs][wear_levelling]")
{
// the functionality of ignoring leading and trailing whitespaces is not implemented yet
// when the feature is implemented, this test will fail
// please, remove the test and implement the functionality in fatfsgen.py to preserve the consistency
test_setup();
test_leading_spaces();
test_teardown();
}
TEST_CASE("(WL) write/read speed test", "[fatfs][wear_levelling][timeout=60]")
{
/* Erase partition before running the test to get consistent results */
const esp_partition_t* part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
esp_partition_erase_range(part, 0, part->size);
test_setup();
const size_t buf_size = 16 * 1024;
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
esp_fill_random(buf, buf_size);
const size_t file_size = 256 * 1024;
const char* file = "/spiflash/256k.bin";
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, true);
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, true);
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, true);
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false);
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false);
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false);
unlink(file);
free(buf);
test_teardown();
}
TEST_CASE("(WL) can get partition info", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_info("/spiflash", "/spiflash/test.txt");
test_teardown();
}
/*
* In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the
* Codepage to CP936 (Simplified Chinese) in order to run the following tests.
* Ensure that the text editor is UTF-8 compatible when compiling these tests.
*/
#if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936)
TEST_CASE("(WL) can read file with UTF-8 encoded strings", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_create_file_with_text("/spiflash/测试文件.txt", fatfs_test_hello_str_utf);
test_fatfs_read_file_utf_8("/spiflash/测试文件.txt");
test_teardown();
}
TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][wear_levelling]")
{
test_setup();
test_fatfs_opendir_readdir_rewinddir_utf_8("/spiflash/目录");
test_teardown();
}
#endif //defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936)
#ifdef CONFIG_SPIRAM
TEST_CASE("FATFS prefers SPI RAM for allocations", "[fatfs]")
{
test_setup();
DIR* dir = opendir("/spiflash");
TEST_ASSERT_NOT_NULL(dir);
TEST_ASSERT(esp_ptr_external_ram(dir));
closedir(dir);
test_teardown();
}
#endif // CONFIG_SPIRAM
@@ -0,0 +1,3 @@
# Name, Type, SubType, Offset, Size, Flags
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 528k,
1 # Name Type SubType Offset Size Flags
2 factory app factory 0x10000 1M
3 storage data fat 528k
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'default',
'release',
'fastseek',
]
)
def test_fatfs_flash_wl_generic(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('*')
dut.expect_unity_test_output(timeout=120)
@pytest.mark.supported_targets
@pytest.mark.psram
@pytest.mark.parametrize(
'config',
[
'psram',
]
)
def test_fatfs_flash_wl_psram(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('*')
dut.expect_unity_test_output(timeout=120)
@@ -0,0 +1,2 @@
CONFIG_FATFS_USE_FASTSEEK=y
CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE=64
@@ -0,0 +1,3 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
@@ -0,0 +1,2 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -0,0 +1,18 @@
# General options for additional checks
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
# disable task watchdog since this app uses an interactive menu
CONFIG_ESP_TASK_WDT_INIT=n
# use custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
# some tests verify file name encoding
CONFIG_FATFS_API_ENCODING_UTF_8=y
CONFIG_FATFS_CODEPAGE_936=y
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../test_fatfs_common")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_fatfs_sdcard)
+14
View File
@@ -0,0 +1,14 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
This test app runs a few FATFS test cases in a FAT-formatted SD card.
These tests require a development board with an SD card slot:
* ESP32-WROVER-KIT
* ESP32-S2 USB_OTG
* ESP32-C3-DevKit-C with an SD card breakout board
The test cases are split between `[sdmmc]` and `[sdspi]`. Only a few tests are executed for sdspi, though. The app could be refactored to ensure that a similar set of tests runs for sdmmc and sdspi.
See [../README.md](../README.md) for more information about FATFS test apps.
@@ -0,0 +1,8 @@
idf_component_register(SRCS "test_fatfs_sdcard_main.c" "test_fatfs_sdspi.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity fatfs vfs sdmmc driver test_fatfs_common
WHOLE_ARCHIVE)
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
target_sources(${COMPONENT_LIB} PRIVATE "test_fatfs_sdmmc.c")
endif()
@@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
void app_main(void)
{
unity_run_menu();
}
@@ -0,0 +1,364 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include "unity.h"
#include "esp_log.h"
#include "esp_random.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdmmc_defs.h"
#include "sdmmc_cmd.h"
#include "ff.h"
#include "test_fatfs_common.h"
#include "soc/soc_caps.h"
#if CONFIG_IDF_TARGET_ESP32
#define SDSPI_MISO_PIN 2
#define SDSPI_MOSI_PIN 15
#define SDSPI_CLK_PIN 14
#define SDSPI_CS_PIN 13
#elif CONFIG_IDF_TARGET_ESP32S2
// Adapted for internal test board ESP-32-S3-USB-OTG-Ev-BOARD_V1.0 (with ESP32-S2-MINI-1 module)
#define SDSPI_MISO_PIN 37
#define SDSPI_MOSI_PIN 35
#define SDSPI_CLK_PIN 36
#define SDSPI_CS_PIN 34
#elif CONFIG_IDF_TARGET_ESP32C3
#define SDSPI_MISO_PIN 6
#define SDSPI_MOSI_PIN 4
#define SDSPI_CLK_PIN 5
#define SDSPI_CS_PIN 1
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#endif //CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
#ifndef SPI_DMA_CHAN
#define SPI_DMA_CHAN 1
#endif //SPI_DMA_CHAN
#define SDSPI_HOST_ID SPI2_HOST
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
// No runner
#include "driver/sdmmc_host.h"
static void test_setup_sdmmc(sdmmc_card_t **out_card)
{
sdmmc_card_t *card = NULL;
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card));
*out_card = card;
}
static void test_teardown_sdmmc(sdmmc_card_t *card)
{
TEST_ESP_OK(esp_vfs_fat_sdcard_unmount("/sdcard", card));
}
static const char* test_filename = "/sdcard/hello.txt";
TEST_CASE("Mount fails cleanly without card inserted", "[fatfs][ignore]")
{
size_t heap_size;
HEAP_SIZE_CAPTURE(heap_size);
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5
};
for (int i = 0; i < 3; ++i) {
printf("Initializing card, attempt %d\n", i);
esp_err_t err = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL);
printf("err=%d\n", err);
TEST_ESP_ERR(ESP_ERR_TIMEOUT, err);
}
HEAP_SIZE_CHECK(heap_size, 0);
}
TEST_CASE("(SD) can format partition", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
TEST_ESP_OK(esp_vfs_fat_sdcard_format("/sdcard", card));
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
test_fatfs_read_file(test_filename);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can create and write file", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can read file", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
test_fatfs_read_file(test_filename);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can read file with pread()", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
test_fatfs_pread_file(test_filename);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) pwrite() works well", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_pwrite_file(test_filename);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) overwrite and append file", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_overwrite_append(test_filename);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can lseek", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_lseek("/sdcard/seek.txt");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can truncate", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_truncate_file("/sdcard/truncate.txt");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can ftruncate", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_ftruncate_file("/sdcard/ftrunc.txt");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) stat returns correct values", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_stat("/sdcard/stat.txt", "/sdcard");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) utime sets modification time", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_utime("/sdcard/utime.txt", "/sdcard");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) unlink removes a file", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_unlink("/sdcard/unlink.txt");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) link copies a file, rename moves a file", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_link_rename("/sdcard/link");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can create and remove directories", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_mkdir_rmdir("/sdcard/dir");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) can opendir root directory of FS", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_can_opendir("/sdcard");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_opendir_readdir_rewinddir("/sdcard/dir");
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) multiple tasks can use same volume", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_concurrent("/sdcard/f");
test_teardown_sdmmc(card);
}
static void sdmmc_speed_test(void *buf, size_t buf_size, size_t file_size, bool write);
TEST_CASE("(SD) write/read speed test", "[fatfs][sdmmc]")
{
size_t heap_size;
HEAP_SIZE_CAPTURE(heap_size);
const size_t buf_size = 16 * 1024;
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
esp_fill_random(buf, buf_size);
const size_t file_size = 1 * 1024 * 1024;
sdmmc_speed_test(buf, 4 * 1024, file_size, true);
sdmmc_speed_test(buf, 8 * 1024, file_size, true);
sdmmc_speed_test(buf, 16 * 1024, file_size, true);
sdmmc_speed_test(buf, 4 * 1024, file_size, false);
sdmmc_speed_test(buf, 8 * 1024, file_size, false);
sdmmc_speed_test(buf, 16 * 1024, file_size, false);
free(buf);
HEAP_SIZE_CHECK(heap_size, 0);
}
static void sdmmc_speed_test(void *buf, size_t buf_size, size_t file_size, bool write)
{
sdmmc_card_t *card = NULL;
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = write,
.max_files = 5,
.allocation_unit_size = 64 * 1024
};
TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card));
test_fatfs_rw_speed("/sdcard/4mb.bin", buf, buf_size, file_size, write);
TEST_ESP_OK(esp_vfs_fat_sdcard_unmount("/sdcard", card));
}
TEST_CASE("(SD) mount two FAT partitions, SDMMC and WL, at the same time", "[fatfs][sdmmc]")
{
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5
};
const char* filename_sd = "/sdcard/sd.txt";
const char* filename_wl = "/spiflash/wl.txt";
const char* str_sd = "this is sd\n";
const char* str_wl = "this is spiflash\n";
/* Erase flash before the first use */
const esp_partition_t *test_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
TEST_ASSERT_NOT_NULL(test_partition);
esp_partition_erase_range(test_partition, 0, test_partition->size);
/* Mount FATFS in SD can WL at the same time. Create a file on each FS */
wl_handle_t wl_handle = WL_INVALID_HANDLE;
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &mount_config, &wl_handle));
unlink(filename_sd);
unlink(filename_wl);
test_fatfs_create_file_with_text(filename_sd, str_sd);
test_fatfs_create_file_with_text(filename_wl, str_wl);
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", wl_handle));
test_teardown_sdmmc(card);
/* Check that the file "sd.txt" was created on FS in SD, and has the right data */
test_setup_sdmmc(&card);
TEST_ASSERT_NULL(fopen(filename_wl, "r"));
FILE* f = fopen(filename_sd, "r");
TEST_ASSERT_NOT_NULL(f);
char buf[64];
TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f));
TEST_ASSERT_EQUAL(0, strcmp(buf, str_sd));
fclose(f);
test_teardown_sdmmc(card);
/* Check that the file "wl.txt" was created on FS in WL, and has the right data */
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &mount_config, &wl_handle));
TEST_ASSERT_NULL(fopen(filename_sd, "r"));
f = fopen(filename_wl, "r");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f));
TEST_ASSERT_EQUAL(0, strcmp(buf, str_wl));
fclose(f);
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", wl_handle));
}
/*
* In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the
* Codepage to CP936 (Simplified Chinese) in order to run the following tests.
* Ensure that the text editor is UTF-8 compatible when compiling these tests.
*/
#if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936)
static const char* test_filename_utf_8 = "/sdcard/测试文件.txt";
TEST_CASE("(SD) can read file using UTF-8 encoded strings", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_create_file_with_text(test_filename_utf_8, fatfs_test_hello_str_utf);
test_fatfs_read_file_utf_8(test_filename_utf_8);
test_teardown_sdmmc(card);
}
TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][ignore]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_opendir_readdir_rewinddir_utf_8("/sdcard/目录");
test_teardown_sdmmc(card);
}
#endif // CONFIG_FATFS_API_ENCODING_UTF_8 && CONFIG_FATFS_CODEPAGE == 936
TEST_CASE("(SD) can get partition info", "[fatfs][sdmmc]")
{
sdmmc_card_t *card = NULL;
test_setup_sdmmc(&card);
test_fatfs_info("/sdcard", "/sdcard/test.txt");
test_teardown_sdmmc(card);
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
@@ -0,0 +1,199 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include "unity.h"
#include "esp_log.h"
#include "esp_random.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdmmc_defs.h"
#include "sdmmc_cmd.h"
#include "ff.h"
#include "test_fatfs_common.h"
#include "soc/soc_caps.h"
#if CONFIG_IDF_TARGET_ESP32
#define SDSPI_MISO_PIN 2
#define SDSPI_MOSI_PIN 15
#define SDSPI_CLK_PIN 14
#define SDSPI_CS_PIN 13
#elif CONFIG_IDF_TARGET_ESP32S2
// Adapted for internal test board ESP-32-S3-USB-OTG-Ev-BOARD_V1.0 (with ESP32-S2-MINI-1 module)
#define SDSPI_MISO_PIN 37
#define SDSPI_MOSI_PIN 35
#define SDSPI_CLK_PIN 36
#define SDSPI_CS_PIN 34
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6
#define SDSPI_MISO_PIN 6
#define SDSPI_MOSI_PIN 4
#define SDSPI_CLK_PIN 5
#define SDSPI_CS_PIN 1
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#elif CONFIG_IDF_TARGET_ESP32H2
#define SDSPI_MISO_PIN 0
#define SDSPI_MOSI_PIN 5
#define SDSPI_CLK_PIN 4
#define SDSPI_CS_PIN 1
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#endif
#ifndef SPI_DMA_CHAN
#define SPI_DMA_CHAN 1
#endif //SPI_DMA_CHAN
#define SDSPI_HOST_ID SPI2_HOST
typedef struct sdspi_mem {
size_t heap_size;
uint32_t* buf;
} sdspi_mem_t;
static const char* s_test_filename = "/sdcard/hello.txt";
static void sdspi_speed_test(void *buf, size_t buf_size, size_t file_size, bool write);
static void test_setup_sdspi(sdspi_mem_t* mem)
{
HEAP_SIZE_CAPTURE(mem->heap_size);
const size_t buf_size = 16 * 1024;
mem->buf = (uint32_t*) calloc(1, buf_size);
esp_fill_random(mem->buf, buf_size);
spi_bus_config_t bus_cfg = {
.mosi_io_num = SDSPI_MOSI_PIN,
.miso_io_num = SDSPI_MISO_PIN,
.sclk_io_num = SDSPI_CLK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
esp_err_t err = spi_bus_initialize(SDSPI_HOST_ID, &bus_cfg, SPI_DMA_CHAN);
TEST_ESP_OK(err);
}
static void test_teardown_sdspi(sdspi_mem_t* mem)
{
free(mem->buf);
spi_bus_free(SDSPI_HOST_ID);
HEAP_SIZE_CHECK(mem->heap_size, 0);
}
TEST_CASE("(SDSPI) write/read speed test", "[fatfs][sdspi]")
{
sdspi_mem_t mem;
size_t file_size = 1 * 1024 * 1024;
test_setup_sdspi(&mem);
sdspi_speed_test(mem.buf, 4 * 1024, file_size, true);
sdspi_speed_test(mem.buf, 8 * 1024, file_size, true);
sdspi_speed_test(mem.buf, 16 * 1024, file_size, true);
sdspi_speed_test(mem.buf, 4 * 1024, file_size, false);
sdspi_speed_test(mem.buf, 8 * 1024, file_size, false);
sdspi_speed_test(mem.buf, 16 * 1024, file_size, false);
test_teardown_sdspi(&mem);
}
static void sdspi_speed_test(void *buf, size_t buf_size, size_t file_size, bool write)
{
const char path[] = "/sdcard";
sdmmc_card_t *card;
card = NULL;
sdspi_device_config_t device_cfg = {
.gpio_cs = SDSPI_CS_PIN,
.host_id = SDSPI_HOST_ID,
.gpio_cd = SDSPI_SLOT_NO_CD,
.gpio_wp = SDSPI_SLOT_NO_WP,
.gpio_int = SDSPI_SLOT_NO_INT,
};
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SDSPI_HOST_ID;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = write,
.max_files = 5,
.allocation_unit_size = 64 * 1024
};
TEST_ESP_OK(esp_vfs_fat_sdspi_mount(path, &host, &device_cfg, &mount_config, &card));
test_fatfs_rw_speed("/sdcard/4mb.bin", buf, buf_size, file_size, write);
TEST_ESP_OK(esp_vfs_fat_sdcard_unmount(path, card));
}
TEST_CASE("(SDSPI) can get partition info", "[fatfs][sdspi]")
{
sdspi_mem_t mem;
test_setup_sdspi(&mem);
const char path[] = "/sdcard";
sdmmc_card_t *card;
card = NULL;
sdspi_device_config_t device_cfg = {
.gpio_cs = SDSPI_CS_PIN,
.host_id = SDSPI_HOST_ID,
.gpio_cd = SDSPI_SLOT_NO_CD,
.gpio_wp = SDSPI_SLOT_NO_WP,
.gpio_int = SDSPI_SLOT_NO_INT,
};
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SDSPI_HOST_ID;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 64 * 1024
};
TEST_ESP_OK(esp_vfs_fat_sdspi_mount(path, &host, &device_cfg, &mount_config, &card));
test_fatfs_info("/sdcard", "/sdcard/test.txt");
TEST_ESP_OK(esp_vfs_fat_sdcard_unmount(path, card));
test_teardown_sdspi(&mem);
}
TEST_CASE("(SDSPI) can format card", "[fatfs][sdspi]")
{
sdspi_mem_t mem;
test_setup_sdspi(&mem);
const char path[] = "/sdcard";
sdmmc_card_t *card;
card = NULL;
sdspi_device_config_t device_cfg = {
.gpio_cs = SDSPI_CS_PIN,
.host_id = SDSPI_HOST_ID,
.gpio_cd = SDSPI_SLOT_NO_CD,
.gpio_wp = SDSPI_SLOT_NO_WP,
.gpio_int = SDSPI_SLOT_NO_INT,
};
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SDSPI_HOST_ID;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 64 * 1024
};
TEST_ESP_OK(esp_vfs_fat_sdspi_mount(path, &host, &device_cfg, &mount_config, &card));
TEST_ESP_OK(esp_vfs_fat_sdcard_format("/sdcard", card));
test_fatfs_create_file_with_text(s_test_filename, fatfs_test_hello_str);
test_fatfs_read_file(s_test_filename);
TEST_ESP_OK(esp_vfs_fat_sdcard_unmount(path, card));
test_teardown_sdspi(&mem);
}
@@ -0,0 +1,3 @@
# Name, Type, SubType, Offset, Size, Flags
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 528k,
1 # Name Type SubType Offset Size Flags
2 factory app factory 0x10000 1M
3 storage data fat 528k
@@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.sdcard_sdmode
@pytest.mark.parametrize(
'config',
[
'default',
'release',
]
)
def test_fatfs_sdcard_generic_sdmmc(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('[sdmmc]')
dut.expect_unity_test_output(timeout=120)
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32c3
@pytest.mark.sdcard_spimode
@pytest.mark.parametrize(
'config',
[
'default',
'release',
]
)
def test_fatfs_sdcard_generic_sdspi(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('[sdspi]')
dut.expect_unity_test_output(timeout=120)
@pytest.mark.esp32
@pytest.mark.sdcard_sdmode
@pytest.mark.psram
@pytest.mark.parametrize(
'config',
[
'psram',
]
)
def test_fatfs_sdcard_psram_sdmmc(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('[sdmmc]')
dut.expect_unity_test_output(timeout=120)
@pytest.mark.esp32
@pytest.mark.sdcard_spimode
@pytest.mark.psram
@pytest.mark.parametrize(
'config',
[
'psram',
]
)
def test_fatfs_sdcard_psram_sdspi(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('')
dut.expect_exact('Enter test for running.')
dut.write('[sdspi]')
dut.expect_unity_test_output(timeout=120)
@@ -0,0 +1,3 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
@@ -0,0 +1,2 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -0,0 +1,19 @@
# General options for additional checks
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
# disable task watchdog since this app uses an interactive menu
CONFIG_ESP_TASK_WDT_INIT=n
# some tests verify file name encoding
CONFIG_FATFS_API_ENCODING_UTF_8=y
CONFIG_FATFS_CODEPAGE_936=y
# some of the tests verify concurrent operation of FAT partitions in
# an SD card and in Flash, so need to use a custom partition table.
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
@@ -0,0 +1,3 @@
idf_component_register(SRCS "test_fatfs_common.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity fatfs vfs unity)
@@ -0,0 +1,979 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <utime.h>
#include "unity.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "test_fatfs_common.h"
const char* fatfs_test_hello_str = "Hello, World!\n";
const char* fatfs_test_hello_str_utf = "世界,你好!\n";
void test_fatfs_create_file_with_text(const char* name, const char* text)
{
FILE* f = fopen(name, "wb");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_TRUE(fputs(text, f) != EOF);
TEST_ASSERT_EQUAL(0, fclose(f));
}
void test_fatfs_create_file_with_o_creat_flag(const char* filename)
{
const int fd = open(filename, O_CREAT|O_WRONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
const int r = pwrite(fd, fatfs_test_hello_str, strlen(fatfs_test_hello_str), 0); //offset=0
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
TEST_ASSERT_EQUAL(0, close(fd));
}
void test_fatfs_open_file_with_o_creat_flag(const char* filename)
{
char buf[32] = { 0 };
const int fd = open(filename, O_CREAT|O_RDONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
int r = pread(fd, buf, sizeof(buf), 0); // it is a regular read() with offset==0
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
TEST_ASSERT_EQUAL(0, close(fd));
}
void test_fatfs_overwrite_append(const char* filename)
{
/* Create new file with 'aaaa' */
test_fatfs_create_file_with_text(filename, "aaaa");
/* Append 'bbbb' to file */
FILE *f_a = fopen(filename, "a");
TEST_ASSERT_NOT_NULL(f_a);
TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
TEST_ASSERT_EQUAL(0, fclose(f_a));
/* Read back 8 bytes from file, verify it's 'aaaabbbb' */
char buf[10] = { 0 };
FILE *f_r = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f_r);
TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
/* Be sure we're at end of file */
TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
TEST_ASSERT_EQUAL(0, fclose(f_r));
/* Overwrite file with 'cccc' */
test_fatfs_create_file_with_text(filename, "cccc");
/* Verify file now only contains 'cccc' */
f_r = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f_r);
bzero(buf, sizeof(buf));
TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
TEST_ASSERT_EQUAL(0, fclose(f_r));
}
void test_fatfs_read_file(const char* filename)
{
FILE* f = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f);
char buf[32] = { 0 };
int cb = fread(buf, 1, sizeof(buf), f);
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), cb);
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
TEST_ASSERT_EQUAL(0, fclose(f));
}
void test_fatfs_read_file_utf_8(const char* filename)
{
FILE* f = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f);
char buf[64] = { 0 }; //Doubled buffer size to allow for longer UTF-8 strings
int cb = fread(buf, 1, sizeof(buf), f);
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str_utf), cb);
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str_utf, buf));
TEST_ASSERT_EQUAL(0, fclose(f));
}
void test_fatfs_pread_file(const char* filename)
{
char buf[32] = { 0 };
const int fd = open(filename, O_RDONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
int r = pread(fd, buf, sizeof(buf), 0); // it is a regular read() with offset==0
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
memset(buf, 0, sizeof(buf));
r = pread(fd, buf, sizeof(buf), 1); // offset==1
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 1, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 1, r);
memset(buf, 0, sizeof(buf));
r = pread(fd, buf, sizeof(buf), 5); // offset==5
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 5, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 5, r);
// regular read() should work now because pread() should not affect the current position in file
memset(buf, 0, sizeof(buf));
r = read(fd, buf, sizeof(buf)); // note that this is read() and not pread()
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
memset(buf, 0, sizeof(buf));
r = pread(fd, buf, sizeof(buf), 10); // offset==10
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 10, buf));
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 10, r);
memset(buf, 0, sizeof(buf));
r = pread(fd, buf, sizeof(buf), strlen(fatfs_test_hello_str) + 1); // offset to EOF
TEST_ASSERT_EQUAL(0, r);
TEST_ASSERT_EQUAL(0, close(fd));
}
static void test_pwrite(const char *filename, off_t offset, const char *msg)
{
const int fd = open(filename, O_WRONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
const off_t current_pos = lseek(fd, 0, SEEK_END); // O_APPEND is not the same - jumps to the end only before write()
const int r = pwrite(fd, msg, strlen(msg), offset);
TEST_ASSERT_EQUAL(strlen(msg), r);
TEST_ASSERT_EQUAL(current_pos, lseek(fd, 0, SEEK_CUR)); // pwrite should not move the pointer
TEST_ASSERT_EQUAL(0, close(fd));
}
static void test_file_content(const char *filename, const char *msg)
{
char buf[32] = { 0 };
const int fd = open(filename, O_RDONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
int r = read(fd, buf, sizeof(buf));
TEST_ASSERT_NOT_EQUAL(-1, r);
TEST_ASSERT_EQUAL(0, strcmp(msg, buf));
TEST_ASSERT_EQUAL(0, close(fd));
}
void test_fatfs_pwrite_file(const char *filename)
{
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
TEST_ASSERT_NOT_EQUAL(-1, fd);
TEST_ASSERT_EQUAL(0, close(fd));
test_pwrite(filename, 0, "Hello");
test_file_content(filename, "Hello");
test_pwrite(filename, strlen("Hello"), ", world!");
test_file_content(filename, "Hello, world!");
test_pwrite(filename, strlen("Hello, "), "Dolly");
test_file_content(filename, "Hello, Dolly!");
}
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count)
{
FILE** files = calloc(files_count, sizeof(FILE*));
for (size_t i = 0; i < files_count; ++i) {
char name[32];
snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
files[i] = fopen(name, "w");
TEST_ASSERT_NOT_NULL(files[i]);
}
/* close everything and clean up */
for (size_t i = 0; i < files_count; ++i) {
fclose(files[i]);
}
free(files);
}
void test_fatfs_lseek(const char* filename)
{
FILE* f = fopen(filename, "wb+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
TEST_ASSERT_EQUAL('9', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
TEST_ASSERT_EQUAL('3', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
TEST_ASSERT_EQUAL('8', fgetc(f));
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END));
TEST_ASSERT_EQUAL(14, ftell(f));
TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
TEST_ASSERT_EQUAL(18, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
char buf[20];
TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f));
const char ref_buf[] = "0123456789\n\0\0\0abc\n";
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
TEST_ASSERT_EQUAL(0, fclose(f));
#ifdef CONFIG_FATFS_USE_FASTSEEK
f = fopen(filename, "rb+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
TEST_ASSERT_EQUAL(18, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, -4, SEEK_CUR));
TEST_ASSERT_EQUAL(14, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, -14, SEEK_CUR));
TEST_ASSERT_EQUAL(0, ftell(f));
TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f));
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
TEST_ASSERT_EQUAL(0, fclose(f));
#endif
}
void test_fatfs_truncate_file(const char* filename)
{
int read = 0;
int truncated_len = 0;
const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char output[sizeof(input)];
FILE* f = fopen(filename, "wb");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(strlen(input), fprintf(f, input));
TEST_ASSERT_EQUAL(0, fclose(f));
// Extending file beyond size is not supported
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
TEST_ASSERT_EQUAL(errno, EPERM);
TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
TEST_ASSERT_EQUAL(errno, EINVAL);
// Truncating should succeed
const char truncated_1[] = "ABCDEFGHIJ";
truncated_len = strlen(truncated_1);
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
f = fopen(filename, "rb");
TEST_ASSERT_NOT_NULL(f);
memset(output, 0, sizeof(output));
read = fread(output, 1, sizeof(output), f);
TEST_ASSERT_EQUAL(truncated_len, read);
TEST_ASSERT_EQUAL_STRING_LEN(truncated_1, output, truncated_len);
TEST_ASSERT_EQUAL(0, fclose(f));
// Once truncated, the new file size should be the basis
// whether truncation should succeed or not
TEST_ASSERT_EQUAL(-1, truncate(filename, truncated_len + 1));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input)));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
TEST_ASSERT_EQUAL(EINVAL, errno);
// Truncating a truncated file should succeed
const char truncated_2[] = "ABCDE";
truncated_len = strlen(truncated_2);
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
f = fopen(filename, "rb");
TEST_ASSERT_NOT_NULL(f);
memset(output, 0, sizeof(output));
read = fread(output, 1, sizeof(output), f);
TEST_ASSERT_EQUAL(truncated_len, read);
TEST_ASSERT_EQUAL_STRING_LEN(truncated_2, output, truncated_len);
TEST_ASSERT_EQUAL(0, fclose(f));
}
void test_fatfs_ftruncate_file(const char* filename)
{
int truncated_len = 0;
const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char output[sizeof(input)];
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
TEST_ASSERT_NOT_EQUAL(-1, fd);
TEST_ASSERT_EQUAL(strlen(input), write(fd, input, strlen(input)));
// Extending file beyond size is not supported
TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input) + 1));
TEST_ASSERT_EQUAL(errno, EPERM);
TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1));
TEST_ASSERT_EQUAL(errno, EINVAL);
// Truncating should succeed
const char truncated_1[] = "ABCDEFGHIJ";
truncated_len = strlen(truncated_1);
TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len));
TEST_ASSERT_EQUAL(0, close(fd));
// open file for reading and validate the content
fd = open(filename, O_RDONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
memset(output, 0, sizeof(output));
TEST_ASSERT_EQUAL(truncated_len, read(fd, output, sizeof(output)));
TEST_ASSERT_EQUAL_STRING_LEN(truncated_1, output, truncated_len);
TEST_ASSERT_EQUAL(0, close(fd));
// further truncate the file
fd = open(filename, O_WRONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
// Once truncated, the new file size should be the basis
// whether truncation should succeed or not
TEST_ASSERT_EQUAL(-1, ftruncate(fd, truncated_len + 1));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input)));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input) + 1));
TEST_ASSERT_EQUAL(EPERM, errno);
TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1));
TEST_ASSERT_EQUAL(EINVAL, errno);
// Truncating a truncated file should succeed
const char truncated_2[] = "ABCDE";
truncated_len = strlen(truncated_2);
TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len));
TEST_ASSERT_EQUAL(0, close(fd));
// open file for reading and validate the content
fd = open(filename, O_RDONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
memset(output, 0, sizeof(output));
TEST_ASSERT_EQUAL(truncated_len, read(fd, output, sizeof(output)));
TEST_ASSERT_EQUAL_STRING_LEN(truncated_2, output, truncated_len);
TEST_ASSERT_EQUAL(0, close(fd));
}
void test_fatfs_stat(const char* filename, const char* root_dir)
{
struct tm tm = {
.tm_year = 2017 - 1900,
.tm_mon = 11,
.tm_mday = 8,
.tm_hour = 19,
.tm_min = 51,
.tm_sec = 10
};
time_t t = mktime(&tm);
printf("Setting time: %s", asctime(&tm));
struct timeval now = { .tv_sec = t };
settimeofday(&now, NULL);
test_fatfs_create_file_with_text(filename, "foo\n");
struct stat st;
TEST_ASSERT_EQUAL(0, stat(filename, &st));
time_t mtime = st.st_mtime;
struct tm mtm;
localtime_r(&mtime, &mtm);
printf("File time: %s", asctime(&mtm));
TEST_ASSERT(llabs(mtime - t) < 2); // fatfs library stores time with 2 second precision
TEST_ASSERT(st.st_mode & S_IFREG);
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
memset(&st, 0, sizeof(st));
TEST_ASSERT_EQUAL(0, stat(root_dir, &st));
TEST_ASSERT(st.st_mode & S_IFDIR);
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
}
void test_fatfs_mtime_dst(const char* filename, const char* root_dir)
{
struct timeval tv = { 1653638041, 0 };
settimeofday(&tv, NULL);
setenv("TZ", "MST7MDT,M3.2.0,M11.1.0", 1);
tzset();
struct tm tm;
time_t sys_time = tv.tv_sec;
localtime_r(&sys_time, &tm);
printf("Setting time: %s", asctime(&tm));
test_fatfs_create_file_with_text(filename, "foo\n");
struct stat st;
TEST_ASSERT_EQUAL(0, stat(filename, &st));
time_t mtime = st.st_mtime;
struct tm mtm;
localtime_r(&mtime, &mtm);
printf("File time: %s", asctime(&mtm));
TEST_ASSERT(llabs(mtime - sys_time) < 2); // fatfs library stores time with 2 second precision
unsetenv("TZ");
tzset();
}
void test_fatfs_utime(const char* filename, const char* root_dir)
{
struct stat achieved_stat;
struct tm desired_tm;
struct utimbuf desired_time = {
.actime = 0, // access time is not supported
.modtime = 0,
};
time_t false_now = 0;
memset(&desired_tm, 0, sizeof(struct tm));
{
// Setting up a false actual time - used when the file is created and for modification with the current time
desired_tm.tm_mon = 10 - 1;
desired_tm.tm_mday = 31;
desired_tm.tm_year = 2018 - 1900;
desired_tm.tm_hour = 10;
desired_tm.tm_min = 35;
desired_tm.tm_sec = 23;
false_now = mktime(&desired_tm);
struct timeval now = { .tv_sec = false_now };
settimeofday(&now, NULL);
}
test_fatfs_create_file_with_text(filename, "");
// 00:00:00. January 1st, 1980 - FATFS cannot handle earlier dates
desired_tm.tm_mon = 1 - 1;
desired_tm.tm_mday = 1;
desired_tm.tm_year = 1980 - 1900;
desired_tm.tm_hour = 0;
desired_tm.tm_min = 0;
desired_tm.tm_sec = 0;
printf("Testing mod. time: %s", asctime(&desired_tm));
desired_time.modtime = mktime(&desired_tm);
TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
// current time
TEST_ASSERT_EQUAL(0, utime(filename, NULL));
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
TEST_ASSERT_NOT_EQUAL(desired_time.modtime, achieved_stat.st_mtime);
TEST_ASSERT(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
// 23:59:08. December 31st, 2037
desired_tm.tm_mon = 12 - 1;
desired_tm.tm_mday = 31;
desired_tm.tm_year = 2037 - 1900;
desired_tm.tm_hour = 23;
desired_tm.tm_min = 59;
desired_tm.tm_sec = 8;
printf("Testing mod. time: %s", asctime(&desired_tm));
desired_time.modtime = mktime(&desired_tm);
TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
//WARNING: it has the Unix Millenium bug (Y2K38)
// 00:00:00. January 1st, 1970 - FATFS cannot handle years before 1980
desired_tm.tm_mon = 1 - 1;
desired_tm.tm_mday = 1;
desired_tm.tm_year = 1970 - 1900;
desired_tm.tm_hour = 0;
desired_tm.tm_min = 0;
desired_tm.tm_sec = 0;
printf("Testing mod. time: %s", asctime(&desired_tm));
desired_time.modtime = mktime(&desired_tm);
TEST_ASSERT_EQUAL(-1, utime(filename, &desired_time));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
void test_fatfs_unlink(const char* filename)
{
test_fatfs_create_file_with_text(filename, "unlink\n");
TEST_ASSERT_EQUAL(0, unlink(filename));
TEST_ASSERT_NULL(fopen(filename, "r"));
}
void test_fatfs_link_rename(const char* filename_prefix)
{
char name_copy[64];
char name_dst[64];
char name_src[64];
snprintf(name_copy, sizeof(name_copy), "%s_cpy.txt", filename_prefix);
snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
unlink(name_copy);
unlink(name_dst);
unlink(name_src);
FILE* f = fopen(name_src, "w+");
TEST_ASSERT_NOT_NULL(f);
const char* str = "0123456789";
for (int i = 0; i < 4000; ++i) {
TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
}
TEST_ASSERT_EQUAL(0, fclose(f));
TEST_ASSERT_EQUAL(0, link(name_src, name_copy));
FILE* fcopy = fopen(name_copy, "r");
TEST_ASSERT_NOT_NULL(fcopy);
TEST_ASSERT_EQUAL(0, fseek(fcopy, 0, SEEK_END));
TEST_ASSERT_EQUAL(40000, ftell(fcopy));
TEST_ASSERT_EQUAL(0, fclose(fcopy));
TEST_ASSERT_EQUAL(0, rename(name_copy, name_dst));
TEST_ASSERT_NULL(fopen(name_copy, "r"));
FILE* fdst = fopen(name_dst, "r");
TEST_ASSERT_NOT_NULL(fdst);
TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
TEST_ASSERT_EQUAL(40000, ftell(fdst));
TEST_ASSERT_EQUAL(0, fclose(fdst));
}
void test_fatfs_mkdir_rmdir(const char* filename_prefix)
{
char name_dir1[64];
char name_dir2[64];
char name_dir2_file[64];
snprintf(name_dir1, sizeof(name_dir1), "%s1", filename_prefix);
snprintf(name_dir2, sizeof(name_dir2), "%s2", filename_prefix);
snprintf(name_dir2_file, sizeof(name_dir2_file), "%s2/1.txt", filename_prefix);
TEST_ASSERT_EQUAL(0, mkdir(name_dir1, 0755));
struct stat st;
TEST_ASSERT_EQUAL(0, stat(name_dir1, &st));
TEST_ASSERT_TRUE(st.st_mode & S_IFDIR);
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
TEST_ASSERT_EQUAL(0, rmdir(name_dir1));
TEST_ASSERT_EQUAL(-1, stat(name_dir1, &st));
TEST_ASSERT_EQUAL(0, mkdir(name_dir2, 0755));
test_fatfs_create_file_with_text(name_dir2_file, "foo\n");
TEST_ASSERT_EQUAL(0, stat(name_dir2, &st));
TEST_ASSERT_TRUE(st.st_mode & S_IFDIR);
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
TEST_ASSERT_EQUAL(0, stat(name_dir2_file, &st));
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
TEST_ASSERT_TRUE(st.st_mode & S_IFREG);
TEST_ASSERT_EQUAL(-1, rmdir(name_dir2));
TEST_ASSERT_EQUAL(0, unlink(name_dir2_file));
TEST_ASSERT_EQUAL(0, rmdir(name_dir2));
}
void test_fatfs_can_opendir(const char* path)
{
char name_dir_file[64];
const char * file_name = "test_opd.txt";
snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
unlink(name_dir_file);
test_fatfs_create_file_with_text(name_dir_file, "test_opendir\n");
DIR* dir = opendir(path);
TEST_ASSERT_NOT_NULL(dir);
bool found = false;
while (true) {
struct dirent* de = readdir(dir);
if (!de) {
break;
}
if (strcasecmp(de->d_name, file_name) == 0) {
found = true;
break;
}
}
TEST_ASSERT_TRUE(found);
TEST_ASSERT_EQUAL(0, closedir(dir));
unlink(name_dir_file);
}
void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix)
{
char name_dir_inner_file[64];
char name_dir_inner[64];
char name_dir_file3[64];
char name_dir_file2[64];
char name_dir_file1[64];
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
unlink(name_dir_inner_file);
rmdir(name_dir_inner);
unlink(name_dir_file1);
unlink(name_dir_file2);
unlink(name_dir_file3);
rmdir(dir_prefix);
TEST_ASSERT_EQUAL(0, mkdir(dir_prefix, 0755));
test_fatfs_create_file_with_text(name_dir_file1, "1\n");
test_fatfs_create_file_with_text(name_dir_file2, "2\n");
test_fatfs_create_file_with_text(name_dir_file3, "\01\02\03");
TEST_ASSERT_EQUAL(0, mkdir(name_dir_inner, 0755));
test_fatfs_create_file_with_text(name_dir_inner_file, "3\n");
DIR* dir = opendir(dir_prefix);
TEST_ASSERT_NOT_NULL(dir);
int count = 0;
const char* names[4];
while(count < 4) {
struct dirent* de = readdir(dir);
if (!de) {
break;
}
printf("found '%s'\n", de->d_name);
if (strcasecmp(de->d_name, "1.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "1.txt";
++count;
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "2.txt";
++count;
} else if (strcasecmp(de->d_name, "inner") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
names[count] = "inner";
++count;
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "boo.bin";
++count;
} else {
TEST_FAIL_MESSAGE("unexpected directory entry");
}
}
TEST_ASSERT_EQUAL(count, 4);
rewinddir(dir);
struct dirent* de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
seekdir(dir, 3);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
seekdir(dir, 1);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
seekdir(dir, 2);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
TEST_ASSERT_EQUAL(0, closedir(dir));
}
void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix)
{
char name_dir_inner_file[64];
char name_dir_inner[64];
char name_dir_file3[64];
char name_dir_file2[64];
char name_dir_file1[64];
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/内部目录/内部文件.txt", dir_prefix);
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/内部目录", dir_prefix);
snprintf(name_dir_file3, sizeof(name_dir_file3), "%s/文件三.bin", dir_prefix);
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/文件二.txt", dir_prefix);
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/文件一.txt", dir_prefix);
unlink(name_dir_inner_file);
rmdir(name_dir_inner);
unlink(name_dir_file1);
unlink(name_dir_file2);
unlink(name_dir_file3);
rmdir(dir_prefix);
TEST_ASSERT_EQUAL(0, mkdir(dir_prefix, 0755));
test_fatfs_create_file_with_text(name_dir_file1, "一号\n");
test_fatfs_create_file_with_text(name_dir_file2, "二号\n");
test_fatfs_create_file_with_text(name_dir_file3, "\0\0\0");
TEST_ASSERT_EQUAL(0, mkdir(name_dir_inner, 0755));
test_fatfs_create_file_with_text(name_dir_inner_file, "三号\n");
DIR* dir = opendir(dir_prefix);
TEST_ASSERT_NOT_NULL(dir);
int count = 0;
const char* names[4];
while(count < 4) {
struct dirent* de = readdir(dir);
if (!de) {
break;
}
printf("found '%s'\n", de->d_name);
if (strcasecmp(de->d_name, "文件一.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "文件一.txt";
++count;
} else if (strcasecmp(de->d_name, "文件二.txt") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "文件二.txt";
++count;
} else if (strcasecmp(de->d_name, "内部目录") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
names[count] = "内部目录";
++count;
} else if (strcasecmp(de->d_name, "文件三.bin") == 0) {
TEST_ASSERT_TRUE(de->d_type == DT_REG);
names[count] = "文件三.bin";
++count;
} else {
TEST_FAIL_MESSAGE("unexpected directory entry");
}
}
TEST_ASSERT_EQUAL(count, 4);
rewinddir(dir);
struct dirent* de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
seekdir(dir, 3);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
seekdir(dir, 1);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
seekdir(dir, 2);
de = readdir(dir);
TEST_ASSERT_NOT_NULL(de);
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
TEST_ASSERT_EQUAL(0, closedir(dir));
}
typedef struct {
const char* filename;
bool write;
size_t word_count;
unsigned seed;
SemaphoreHandle_t done;
esp_err_t result;
} read_write_test_arg_t;
#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
{ \
.filename = name, \
.seed = seed_, \
.word_count = 8192, \
.write = true, \
.done = xSemaphoreCreateBinary() \
}
static void read_write_task(void* param)
{
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
if (f == NULL) {
args->result = ESP_ERR_NOT_FOUND;
goto done;
}
srand(args->seed);
for (size_t i = 0; i < args->word_count; ++i) {
unsigned val = rand();
if (args->write) {
int cnt = fwrite(&val, sizeof(val), 1, f);
if (cnt != 1) {
printf("E(w): i=%d, cnt=%d val=0x08%x\n", i, cnt, val);
args->result = ESP_FAIL;
goto close;
}
} else {
unsigned rval;
int cnt = fread(&rval, sizeof(rval), 1, f);
if (cnt != 1 || rval != val) {
printf("E(r): i=%d, cnt=%d rval=0x08%x val=0x08%x\n", i, cnt, rval, val);
args->result = ESP_FAIL;
goto close;
}
}
}
args->result = ESP_OK;
close:
fclose(f);
done:
xSemaphoreGive(args->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
void test_fatfs_concurrent(const char* filename_prefix)
{
char names[4][64];
for (size_t i = 0; i < 4; ++i) {
snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
unlink(names[i]);
}
read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
printf("writing f1 and f2\n");
const int cpuid_0 = 0;
const int cpuid_1 = portNUM_PROCESSORS - 1;
const int stack_size = 4096;
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
xSemaphoreTake(args1.done, portMAX_DELAY);
printf("f1 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
xSemaphoreTake(args2.done, portMAX_DELAY);
printf("f2 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
args1.write = false;
args2.write = false;
read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
printf("reading f1 and f2, writing f3 and f4\n");
xTaskCreatePinnedToCore(&read_write_task, "rw3", stack_size, &args3, 3, NULL, cpuid_1);
xTaskCreatePinnedToCore(&read_write_task, "rw4", stack_size, &args4, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
xSemaphoreTake(args1.done, portMAX_DELAY);
printf("f1 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
xSemaphoreTake(args2.done, portMAX_DELAY);
printf("f2 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
xSemaphoreTake(args3.done, portMAX_DELAY);
printf("f3 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
xSemaphoreTake(args4.done, portMAX_DELAY);
printf("f4 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
vSemaphoreDelete(args1.done);
vSemaphoreDelete(args2.done);
vSemaphoreDelete(args3.done);
vSemaphoreDelete(args4.done);
}
void test_leading_spaces(void){
// fatfs should ignore leading and trailing whitespaces
// and files "/spiflash/ thelongfile.txt " and "/spiflash/thelongfile.txt" should be equivalent
// this feature is currently not implemented
FILE* f = fopen( "/spiflash/ thelongfile.txt ", "wb");
fclose(f);
TEST_ASSERT_NULL(fopen("/spiflash/thelongfile.txt", "r"));
}
void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool is_write)
{
const size_t buf_count = file_size / buf_size;
FILE* f = fopen(filename, (is_write) ? "wb" : "rb");
TEST_ASSERT_NOT_NULL(f);
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
for (size_t n = 0; n < buf_count; ++n) {
if (is_write) {
TEST_ASSERT_EQUAL(buf_size, write(fileno(f), buf, buf_size));
} else {
if (read(fileno(f), buf, buf_size) != buf_size) {
printf("reading at n=%d, eof=%d", n, feof(f));
TEST_FAIL();
}
}
}
struct timeval tv_end;
gettimeofday(&tv_end, NULL);
TEST_ASSERT_EQUAL(0, fclose(f));
float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec);
printf("%s %d bytes (block size %d) in %.3fms (%.3f MB/s)\n",
(is_write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3,
file_size / (1024.0f * 1024.0f * t_s));
}
void test_fatfs_info(const char* base_path, const char* filepath)
{
// Empty FS
uint64_t total_bytes = 0;
uint64_t free_bytes = 0;
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_fat_info(base_path, &total_bytes, &free_bytes));
ESP_LOGD("fatfs info", "total_bytes=%llu, free_bytes=%llu", total_bytes, free_bytes);
TEST_ASSERT_NOT_EQUAL(0, total_bytes);
// FS with a file
FILE* f = fopen(filepath, "wb");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_TRUE(fputs(fatfs_test_hello_str, f) != EOF);
TEST_ASSERT_EQUAL(0, fclose(f));
uint64_t free_bytes_new = 0;
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_fat_info(base_path, &total_bytes, &free_bytes_new));
ESP_LOGD("fatfs info", "total_bytes=%llu, free_bytes_new=%llu", total_bytes, free_bytes_new);
TEST_ASSERT_NOT_EQUAL(free_bytes, free_bytes_new);
// File removed
TEST_ASSERT_EQUAL(0, remove(filepath));
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_fat_info(base_path, &total_bytes, &free_bytes_new));
ESP_LOGD("fatfs info", "total_bytes=%llu, free_bytes_after_delete=%llu", total_bytes, free_bytes_new);
TEST_ASSERT_EQUAL(free_bytes, free_bytes_new);
}
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file test_fatfs_common.h
* @brief Common routines for FAT-on-SDMMC and FAT-on-WL tests
*/
#define HEAP_SIZE_CAPTURE(heap_size) \
heap_size = esp_get_free_heap_size();
#define HEAP_SIZE_CHECK(heap_size, tolerance) \
do {\
size_t final_heap_size = esp_get_free_heap_size(); \
if (final_heap_size < heap_size - tolerance) { \
printf("Initial heap size: %d, final: %d, diff=%d\n", heap_size, final_heap_size, heap_size - final_heap_size); \
} \
} while(0)
extern const char* fatfs_test_hello_str;
extern const char* fatfs_test_hello_str_utf;
void test_fatfs_create_file_with_text(const char* name, const char* text);
void test_fatfs_create_file_with_o_creat_flag(const char* filename);
void test_fatfs_open_file_with_o_creat_flag(const char* filename);
void test_fatfs_overwrite_append(const char* filename);
void test_fatfs_read_file(const char* filename);
void test_fatfs_read_file_utf_8(const char* filename);
void test_fatfs_pread_file(const char* filename);
void test_fatfs_pwrite_file(const char* filename);
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
void test_fatfs_lseek(const char* filename);
void test_fatfs_truncate_file(const char* path);
void test_fatfs_ftruncate_file(const char* path);
void test_fatfs_stat(const char* filename, const char* root_dir);
void test_fatfs_mtime_dst(const char* filename, const char* root_dir);
void test_fatfs_utime(const char* filename, const char* root_dir);
void test_fatfs_unlink(const char* filename);
void test_fatfs_link_rename(const char* filename_prefix);
void test_fatfs_concurrent(const char* filename_prefix);
void test_fatfs_mkdir_rmdir(const char* filename_prefix);
void test_fatfs_can_opendir(const char* path);
void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix);
void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix);
void test_leading_spaces(void);
void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool write);
void test_fatfs_info(const char* base_path, const char* filepath);