MeshTalos-Client/managed_components/joltwallet__littlefs/test/test_littlefs.c
2025-12-03 14:20:11 +08:00

941 lines
30 KiB
C

//#define LOG_LOCAL_LEVEL 4
#include "test_littlefs_common.h"
static void test_littlefs_write_file_with_offset(const char *filename);
static void test_littlefs_read_file_with_offset(const char *filename);
static void test_littlefs_overwrite_append(const char* filename);
static void test_littlefs_open_max_files(const char* filename_prefix, size_t files_count);
static void test_littlefs_concurrent_rw(const char* filename_prefix);
static int test_littlefs_stat(const char *path, struct stat *buf);
TEST_CASE("can initialize LittleFS in erased partition", "[littlefs]")
{
/* Gets the partition labeled "flash_test" */
const esp_partition_t* part = get_test_data_partition();
TEST_ASSERT_NOT_NULL(part);
TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
test_setup();
size_t total = 0, used = 0;
TEST_ESP_OK(esp_littlefs_info(littlefs_test_partition_label, &total, &used));
printf("total: %d, used: %d\n", total, used);
TEST_ASSERT_EQUAL(8192, used); // 2 blocks are used on a fresh filesystem
test_teardown();
}
TEST_CASE("can format mounted partition", "[littlefs]")
{
// Mount LittleFS, create file, format, check that the file does not exist.
const esp_partition_t* part = get_test_data_partition();
TEST_ASSERT_NOT_NULL(part);
test_setup();
const char* filename = littlefs_base_path "/hello.txt";
test_littlefs_create_file_with_text(filename, littlefs_test_hello_str);
printf("Deleting \"%s\" via formatting fs.\n", filename);
esp_littlefs_format(part->label);
FILE* f = fopen(filename, "r");
TEST_ASSERT_NULL(f);
test_teardown();
}
TEST_CASE("can format unmounted partition", "[littlefs]")
{
// Mount LittleFS, create file, unmount. Format. Mount again, check that
// the file does not exist.
const esp_partition_t* part = get_test_data_partition();
TEST_ASSERT_NOT_NULL(part);
test_setup();
const char* filename = littlefs_base_path "/hello.txt";
test_littlefs_create_file_with_text(filename, littlefs_test_hello_str);
test_teardown();
esp_littlefs_format(part->label);
// Don't use test_setup here, need to mount without formatting
const esp_vfs_littlefs_conf_t conf = {
.base_path = littlefs_base_path,
.partition_label = littlefs_test_partition_label,
.format_if_mount_failed = false
};
TEST_ESP_OK(esp_vfs_littlefs_register(&conf));
FILE* f = fopen(filename, "r");
TEST_ASSERT_NULL(f);
test_teardown();
}
TEST_CASE("NULL label mounts first littlefs partition.", "[littlefs]")
{
esp_littlefs_format(littlefs_test_partition_label);
const esp_vfs_littlefs_conf_t conf = {
.base_path = littlefs_base_path,
.partition_label = NULL,
.format_if_mount_failed = true
};
TEST_ESP_OK(esp_vfs_littlefs_register(&conf));
TEST_ASSERT_TRUE( heap_caps_check_integrity_all(true) );
TEST_ASSERT_TRUE( esp_littlefs_mounted(NULL) );
TEST_ASSERT_TRUE( esp_littlefs_mounted("named_part") );
TEST_ESP_OK(esp_vfs_littlefs_unregister(NULL));
TEST_ASSERT_TRUE( heap_caps_check_integrity_all(true) );
}
TEST_CASE("can create and write file", "[littlefs]")
{
test_setup();
test_littlefs_create_file_with_text(littlefs_base_path "/hello.txt", littlefs_test_hello_str);
test_teardown();
}
TEST_CASE("can read file", "[littlefs]")
{
test_setup();
test_littlefs_create_file_with_text(littlefs_base_path "/hello.txt", littlefs_test_hello_str);
test_littlefs_read_file(littlefs_base_path "/hello.txt");
test_teardown();
}
TEST_CASE("can write to file with offset (pwrite)", "[littlefs]")
{
test_setup();
test_littlefs_write_file_with_offset(littlefs_base_path "/hello.txt");
test_teardown();
}
TEST_CASE("can read from file with offset (pread)", "[littlefs]")
{
test_setup();
test_littlefs_read_file_with_offset(littlefs_base_path "/hello.txt");
test_teardown();
}
TEST_CASE("r+ mode read and write file", "[littlefs]")
{
/* Note: despite some online resources, "r+" should not create a file
* if it does not exist */
const char fn[] = littlefs_base_path "/hello.txt";
char buf[100] = { 0 };
test_setup();
test_littlefs_create_file_with_text(fn, "foo");
/* Read back the previously written foo, and add bar*/
{
FILE* f = fopen(fn, "r+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(3, fread(buf, 1, sizeof(buf), f));
TEST_ASSERT_EQUAL_STRING("foo", buf);
TEST_ASSERT_TRUE(fputs("bar", f) != EOF);
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
TEST_ASSERT_EQUAL(6, fread(buf, 1, 6, f));
TEST_ASSERT_EQUAL_STRING("foobar", buf);
TEST_ASSERT_EQUAL(0, fclose(f));
}
/* Just normal read the whole contents */
{
FILE* f = fopen(fn, "r+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(6, fread(buf, 1, sizeof(buf), f));
TEST_ASSERT_EQUAL_STRING("foobar", buf);
TEST_ASSERT_EQUAL(0, fclose(f));
}
test_teardown();
}
TEST_CASE("w+ mode read and write file", "[littlefs]")
{
const char fn[] = littlefs_base_path "/hello.txt";
char buf[100] = { 0 };
test_setup();
test_littlefs_create_file_with_text(fn, "foo");
/* this should overwrite the file and be readable */
{
FILE* f = fopen(fn, "w+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_TRUE(fputs("bar", f) != EOF);
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
TEST_ASSERT_EQUAL(3, fread(buf, 1, sizeof(buf), f));
TEST_ASSERT_EQUAL_STRING("bar", buf);
TEST_ASSERT_EQUAL(0, fclose(f));
}
test_teardown();
}
TEST_CASE("can open maximum number of files", "[littlefs]")
{
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr, esp-idf defaults to maximum 64 file descriptors */
test_setup();
test_littlefs_open_max_files("/littlefs/f", max_files);
test_teardown();
}
TEST_CASE("overwrite and append file", "[littlefs]")
{
test_setup();
test_littlefs_overwrite_append(littlefs_base_path "/hello.txt");
test_teardown();
}
TEST_CASE("use append with other flags", "[littlefs]")
{
// https://github.com/joltwallet/esp_littlefs/issues/154
test_setup();
int fd;
fd = open(littlefs_base_path "/fcntl.txt", O_CREAT | O_WRONLY | O_TRUNC, 0777);
TEST_ASSERT_EQUAL(6, write(fd, "test1\n", 6));
TEST_ASSERT_EQUAL(0, close(fd));
fd = open(littlefs_base_path "/fcntl.txt", O_CREAT | O_WRONLY | O_APPEND, 0777);
TEST_ASSERT_EQUAL(0, lseek(fd, 0, SEEK_CUR));
TEST_ASSERT_EQUAL(6, write(fd, "test2\n", 6));
TEST_ASSERT_EQUAL(12, lseek(fd, 0, SEEK_CUR));
TEST_ASSERT_EQUAL(0, close(fd));
test_teardown();
}
TEST_CASE("can lseek", "[littlefs]")
{
test_setup();
FILE* f = fopen(littlefs_base_path "/seek.txt", "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, 0, SEEK_END));
TEST_ASSERT_EQUAL(11, ftell(f));
// Appending to end
TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
TEST_ASSERT_EQUAL(15, ftell(f));
// Appending past end of file, creating a "hole"
TEST_ASSERT_EQUAL(0, fseek(f, 2, SEEK_END));
TEST_ASSERT_EQUAL(4, fprintf(f, "foo\n"));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
char buf[32];
TEST_ASSERT_EQUAL(21, fread(buf, 1, sizeof(buf), f));
const char ref_buf[] = "0123456789\nabc\n\0\0foo\n";
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
// Error checking
// Attempting to seek before the beginning of file should return an error
TEST_ASSERT_EQUAL(-1, fseek(f, 100, 100)); // Bad mode
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL(-1, fseek(f, -1, SEEK_SET)); // Seeking to before start of file
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL(0, fclose(f));
test_teardown();
}
TEST_CASE("stat/fstat returns correct values", "[littlefs]")
{
test_setup();
const char filename[] = littlefs_base_path "/stat.txt";
test_littlefs_create_file_with_text(filename, "foo\n");
struct stat st;
for(uint8_t i=0; i < 2; i++) {
if(i == 0){
// Test stat
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
}
else {
#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
// Test fstat
FILE *f = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
TEST_ASSERT_EQUAL(0, fclose(f));
#endif
}
TEST_ASSERT(st.st_mode & S_IFREG);
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
TEST_ASSERT_EQUAL(4, st.st_size);
}
test_teardown();
}
TEST_CASE("multiple tasks can use same volume", "[littlefs]")
{
test_setup();
test_littlefs_concurrent_rw(littlefs_base_path "/f");
test_teardown();
}
TEST_CASE("esp_littlefs_info", "[littlefs]")
{
test_setup();
char filename[] = littlefs_base_path "/test_esp_littlefs_info.bin";
unlink(filename); /* Delete the file incase it exists */
/* Get starting system size */
size_t total_og = 0, used_og = 0;
TEST_ESP_OK(esp_littlefs_info(littlefs_test_partition_label, &total_og, &used_og));
/* Write 100,000 bytes */
FILE* f = fopen(filename, "wb");
TEST_ASSERT_NOT_NULL(f);
char val = 'c';
size_t n_bytes = 100000;
for(int i=0; i < n_bytes; i++) {
TEST_ASSERT_EQUAL(1, fwrite(&val, 1, 1, f));
}
TEST_ASSERT_EQUAL(0, fclose(f));
/* Re-check system size */
size_t total_new = 0, used_new = 0;
TEST_ESP_OK(esp_littlefs_info(littlefs_test_partition_label, &total_new, &used_new));
printf("old: %d; new: %d; diff: %d\n", used_og, used_new, used_new-used_og);
/* total amount of storage shouldn't change */
TEST_ASSERT_EQUAL_INT(total_og, total_new);
/* The actual amount of used storage should be within 2 blocks of expected.*/
size_t diff = used_new - used_og;
TEST_ASSERT_GREATER_THAN_INT(n_bytes - (2 * 4096), diff);
TEST_ASSERT_LESS_THAN_INT(n_bytes + (2 * 4096), diff);
unlink(filename);
test_teardown();
}
#if CONFIG_LITTLEFS_USE_MTIME
#if CONFIG_LITTLEFS_MTIME_USE_SECONDS
TEST_CASE("mtime support", "[littlefs]")
{
test_setup();
/* Open a file, check that mtime is set correctly */
const char* filename = littlefs_base_path "/time";
time_t t_before_create = time(NULL);
test_littlefs_create_file_with_text(filename, "test");
time_t t_after_create = time(NULL);
struct stat st;
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT(st.st_mtime >= t_before_create);
TEST_ASSERT(st.st_mtime <= t_after_create);
/* Wait a bit, open & close again, check that mtime is updated */
vTaskDelay(2000 / portTICK_PERIOD_MS);
time_t t_before_close = time(NULL);
FILE *f = fopen(filename, "a");
TEST_ASSERT_EQUAL(0, fclose(f));
time_t t_after_close = time(NULL);
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
printf("mtime=%d\n", (int) st.st_mtime);
time_t append_mtime = st.st_mtime;
TEST_ASSERT(append_mtime >= t_before_close);
TEST_ASSERT(append_mtime <= t_after_close);
/* Wait a bit, open for reading, check that mtime is not updated */
vTaskDelay(2000 / portTICK_PERIOD_MS);
time_t t_before_close_ro = time(NULL);
f = fopen(filename, "r");
TEST_ASSERT_EQUAL(0, fclose(f));
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT(t_before_close_ro > t_after_close); // sufficient time has passed for this test to be valid.
// make sure the st_mtime is the same as bfore
TEST_ASSERT(st.st_mtime == append_mtime);
test_teardown();
}
#endif
#if CONFIG_LITTLEFS_MTIME_USE_NONCE
TEST_CASE("mnonce support", "[littlefs]")
{
/* Open a file, check that mtime is set correctly */
struct stat st;
const char* filename = littlefs_base_path "/time";
test_setup();
test_littlefs_create_file_with_text(filename, "test");
int nonce1;
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
nonce1 = (int) st.st_mtime;
printf("mtime=%d\n", nonce1);
TEST_ASSERT(nonce1 >= 0);
/* open again, check that mtime is updated */
int nonce2;
FILE *f = fopen(filename, "a");
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
nonce2 = (int) st.st_mtime;
printf("mtime=%d\n", nonce2);
if( nonce1 == UINT32_MAX ) {
TEST_ASSERT_EQUAL_INT(1, nonce2);
}
else {
TEST_ASSERT_EQUAL_INT(1, nonce2-nonce1);
}
TEST_ASSERT_EQUAL(0, fclose(f));
/* open for reading, check that mtime is not updated */
int nonce3;
f = fopen(filename, "r");
TEST_ASSERT_EQUAL(0, test_littlefs_stat(filename, &st));
nonce3 = (int) st.st_mtime;
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT_EQUAL_INT(nonce2, nonce3);
TEST_ASSERT_EQUAL(0, fclose(f));
test_teardown();
}
#endif
#endif
static void test_littlefs_write_file_with_offset(const char *filename)
{
const char *source = "Replace this character: [k]";
off_t offset = strstr(source, "k") - source;
size_t len = strlen(source);
const char new_char = 'y';
// Create file with string at source string
test_littlefs_create_file_with_text(filename, source);
// Replace k with y at the file
int fd = open(filename, O_RDWR);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
int written = pwrite(fd, &new_char, 1, offset);
TEST_ASSERT_EQUAL(1, written);
TEST_ASSERT_EQUAL(0, close(fd));
char buf[len];
// Compare if both are equal
FILE *f = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f);
int rd = fread(buf, len, 1, f);
TEST_ASSERT_EQUAL(1, rd);
TEST_ASSERT_EQUAL(buf[offset], new_char);
TEST_ASSERT_EQUAL(0, fclose(f));
}
static void test_littlefs_read_file_with_offset(const char *filename)
{
const char *source = "This text will be partially read";
off_t offset = strstr(source, "p") - source;
size_t len = strlen(source);
char buf[len - offset + 1];
buf[len-offset] = '\0'; // EOS
// Create file with string at source string
test_littlefs_create_file_with_text(filename, source);
// Read file content beginning at `partially` word
int fd = open(filename, O_RDONLY);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
int rd = pread(fd, buf, len - offset, offset);
TEST_ASSERT_EQUAL(len - offset, rd);
// Compare if string read from file and source string related slice are equal
int res = strcmp(buf, &source[offset]);
TEST_ASSERT_EQUAL(0, res);
TEST_ASSERT_EQUAL(0, close(fd));
}
static void test_littlefs_overwrite_append(const char* filename)
{
/* Create new file with 'aaaa' */
test_littlefs_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_littlefs_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));
}
static void test_littlefs_open_max_files(const char* filename_prefix, size_t files_count)
{
FILE** files = calloc(files_count, sizeof(FILE*));
assert(files);
for (size_t i = 0; i < files_count; ++i) {
char name[32];
snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
printf("Opening \"%s\"\n", name);
TEST_ASSERT_TRUE( heap_caps_check_integrity_all(true) );
files[i] = fopen(name, "w");
TEST_ASSERT_NOT_NULL(files[i]);
TEST_ASSERT_TRUE( heap_caps_check_integrity_all(true) );
}
/* close everything and clean up */
for (size_t i = 0; i < files_count; ++i) {
TEST_ASSERT_EQUAL(0, fclose(files[i]));
TEST_ASSERT_TRUE( heap_caps_check_integrity_all(true) );
}
free(files);
}
typedef enum {
CONCURRENT_TASK_ACTION_READ,
CONCURRENT_TASK_ACTION_WRITE,
CONCURRENT_TASK_ACTION_STAT,
} concurrent_task_action_t;
typedef struct {
const char* filename;
concurrent_task_action_t action;
size_t word_count;
int seed;
SemaphoreHandle_t done;
int result;
} read_write_test_arg_t;
#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
{ \
.filename = name, \
.seed = seed_, \
.word_count = 4096, \
.action = CONCURRENT_TASK_ACTION_WRITE, \
.done = xSemaphoreCreateBinary() \
}
static void read_write_task(void* param)
{
FILE *f = NULL;
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
if (args->action == CONCURRENT_TASK_ACTION_WRITE) {
f = fopen(args->filename, "wb");
if (f == NULL) {args->result = ESP_ERR_NOT_FOUND; goto done;}
} else if (args->action == CONCURRENT_TASK_ACTION_READ) {
f = fopen(args->filename, "rb");
if (f == NULL) {args->result = ESP_ERR_NOT_FOUND; goto done;}
} else if (args->action == CONCURRENT_TASK_ACTION_STAT) {
}
srand(args->seed);
for (size_t i = 0; i < args->word_count; ++i) {
uint32_t val = rand();
if (args->action == CONCURRENT_TASK_ACTION_WRITE) {
int cnt = fwrite(&val, sizeof(val), 1, f);
if (cnt != 1) {
esp_rom_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
args->result = ESP_FAIL;
goto close;
}
} else if (args->action == CONCURRENT_TASK_ACTION_READ) {
uint32_t rval;
int cnt = fread(&rval, sizeof(rval), 1, f);
if (cnt != 1) {
esp_rom_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
args->result = ESP_FAIL;
goto close;
}
} else if (args->action == CONCURRENT_TASK_ACTION_STAT) {
int res;
struct stat buf;
res = stat(args->filename, &buf);
if(res < 0) {
args->result = ESP_FAIL;
goto done;
}
}
}
args->result = ESP_OK;
close:
if(f) {
TEST_ASSERT_EQUAL(0, fclose(f));
}
done:
xSemaphoreGive(args->done);
vTaskDelay(1);
vTaskDelete(NULL);
}
static void test_littlefs_concurrent_rw(const char* filename_prefix)
{
#define TASK_SIZE 4096
char names[4][64];
for (size_t i = 0; i < 4; ++i) {
snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
}
/************************************************
* TESTING CONCURRENT WRITES TO DIFFERENT FILES *
************************************************/
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;
xTaskCreatePinnedToCore(&read_write_task, "rw1", TASK_SIZE, &args1, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw2", TASK_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.action = CONCURRENT_TASK_ACTION_READ;
args2.action = CONCURRENT_TASK_ACTION_READ;
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, stating f1 concurrently from 2 cores\n");
xTaskCreatePinnedToCore(&read_write_task, "rw3", TASK_SIZE, &args3, 3, NULL, cpuid_1);
xTaskCreatePinnedToCore(&read_write_task, "rw4", TASK_SIZE, &args4, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw1", TASK_SIZE, &args1, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "rw2", TASK_SIZE, &args2, 3, NULL, cpuid_1);
#if CONFIG_VFS_SUPPORT_DIR
read_write_test_arg_t args5 = READ_WRITE_TEST_ARG_INIT(names[0], 3);
args5.action = CONCURRENT_TASK_ACTION_STAT;
args5.word_count = 300;
read_write_test_arg_t args6 = READ_WRITE_TEST_ARG_INIT(names[0], 3);
args6.action = CONCURRENT_TASK_ACTION_STAT;
args6.word_count = 300;
xTaskCreatePinnedToCore(&read_write_task, "stat1", TASK_SIZE, &args5, 3, NULL, cpuid_0);
xTaskCreatePinnedToCore(&read_write_task, "stat2", TASK_SIZE, &args6, 3, NULL, cpuid_1);
#endif
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");
#if CONFIG_VFS_SUPPORT_DIR
TEST_ASSERT_EQUAL(ESP_OK, args5.result);
xSemaphoreTake(args5.done, portMAX_DELAY);
printf("stat1 done\n");
TEST_ASSERT_EQUAL(ESP_OK, args6.result);
xSemaphoreTake(args6.done, portMAX_DELAY);
printf("stat2 done\n");
#endif
vSemaphoreDelete(args1.done);
vSemaphoreDelete(args2.done);
vSemaphoreDelete(args3.done);
vSemaphoreDelete(args4.done);
#undef TASK_SIZE
}
#if CONFIG_LITTLEFS_SPIFFS_COMPAT
TEST_CASE("SPIFFS COMPAT: file creation and deletion", "[littlefs]")
{
test_setup();
const char* filename = littlefs_base_path "/spiffs_compat/foo/bar/spiffs_compat.bin";
FILE* f = fopen(filename, "w");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_TRUE(fputs("bar", f) != EOF);
TEST_ASSERT_EQUAL(0, fclose(f));
TEST_ASSERT_EQUAL(0, unlink(filename));
/* check to see if all the directories were deleted */
struct stat sb;
if (test_littlefs_stat(littlefs_base_path "/spiffs_compat", &sb) == 0 && S_ISDIR(sb.st_mode)) {
TEST_FAIL_MESSAGE("Empty directories were not deleted");
}
test_teardown();
}
TEST_CASE("SPIFFS COMPAT: file creation and rename", "[littlefs]")
{
test_setup();
int res;
char message[256];
const char* src = littlefs_base_path "/spiffs_compat/src/foo/bar/spiffs_compat.bin";
const char* dst = littlefs_base_path "/spiffs_compat/dst/foo/bar/spiffs_compat.bin";
FILE* f = fopen(src, "w");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_TRUE(fputs("bar", f) != EOF);
TEST_ASSERT_EQUAL(0, fclose(f));
res = rename(src, dst);
snprintf(message, sizeof(message), "errno: %d", errno);
TEST_ASSERT_EQUAL_MESSAGE(0, res, message);
/* check to see if all the directories were deleted */
struct stat sb;
if (test_littlefs_stat(littlefs_base_path "/spiffs_compat/src", &sb) == 0 && S_ISDIR(sb.st_mode)) {
TEST_FAIL_MESSAGE("Empty directories were not deleted");
}
test_teardown();
}
#endif // CONFIG_LITTLEFS_SPIFFS_COMPAT
TEST_CASE("Rewriting file frees space immediately (#7426)", "[littlefs]")
{
/* modified from:
* https://github.com/esp8266/Arduino/commit/c663c55926f205723c3d56dd7030bacbe7960f8e
*/
test_setup();
size_t total = 0, used = 0;
TEST_ESP_OK(esp_littlefs_info(littlefs_test_partition_label, &total, &used));
// 2 block overhead
int kb_to_write = (total - used - (2*4096)) / 1024;
// Create and overwrite a file >50% of spaceA (48/64K)
uint8_t buf[1024];
memset(buf, 0xaa, 1024);
for (uint8_t x = 0; x < 2; x++) {
FILE *f = fopen(littlefs_base_path "/file1.bin", "w");
TEST_ASSERT_NOT_NULL(f);
for (size_t i = 0; i < kb_to_write; i++) {
TEST_ASSERT_EQUAL_INT(1024, fwrite(buf, 1, 1024, f));
}
TEST_ASSERT_EQUAL(0, fclose(f));
}
test_teardown();
}
TEST_CASE("esp_littlefs_info returns used_bytes > total_bytes", "[littlefs]")
{
// https://github.com/joltwallet/esp_littlefs/issues/66
test_setup();
const char foo[] = "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo";
char names[7][64];
for (size_t i = 0; i < 7; ++i) {
snprintf(names[i], sizeof(names[i]), littlefs_base_path "/%d", i + 1);
unlink(names[i]); // Make sure these files don't exist
}
for (size_t i = 0; i < 7; ++i) {
FILE* f = fopen(names[i], "wb");
TEST_ASSERT_NOT_NULL(f);
char val = 'c';
size_t n_bytes = 65432;
for(int i=0; i < n_bytes; i++) {
TEST_ASSERT_EQUAL(1, fwrite(&val, 1, 1, f));
}
TEST_ASSERT_EQUAL(0, fclose(f));
}
bool disk_full = false;
int i = 0;
while(!disk_full){
char *filename = names[i % 7];
FILE* f = fopen(filename, "a+b");
TEST_ASSERT_NOT_NULL(f);
size_t n_bytes = 200 + i % 17;
int amount_written = fwrite(foo, n_bytes, 1, f);
if(amount_written != 1) {
disk_full = true;
}
if(0 != fclose(f)){
disk_full = true;
}
size_t total = 0, used = 0;
TEST_ESP_OK(esp_littlefs_info(littlefs_test_partition_label, &total, &used));
TEST_ASSERT_GREATER_OR_EQUAL_INT(used, total);
//printf("used: %d total: %d\n", used, total);
i++;
}
test_teardown();
}
#if CONFIG_LITTLEFS_OPEN_DIR
TEST_CASE("open with flag O_DIRECTORY", "[littlefs]")
{
int fd;
int ret;
struct stat stat;
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
char path[MAXPATHLEN];
#endif
test_setup();
fd = open("/littlefs/dir1/", O_DIRECTORY | O_NOFOLLOW);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/dir1/", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = fstat(fd, &stat);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_NOT_EQUAL(0, stat.st_size);
TEST_ASSERT_EQUAL(S_IFDIR, stat.st_mode);
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
fd = open("/littlefs/dir1/dir2/", O_DIRECTORY | O_NOFOLLOW);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/dir1/dir2/", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = fstat(fd, &stat);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_NOT_EQUAL(0, stat.st_size);
TEST_ASSERT_EQUAL(S_IFDIR, stat.st_mode);
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
fd = open("/littlefs/dir1/dir2/test.txt", O_CREAT | O_RDWR);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/dir1/dir2/test.txt", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = fstat(fd, &stat);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL(0, stat.st_size);
TEST_ASSERT_EQUAL(S_IFREG, stat.st_mode);
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
/* File is created in previous step */
fd = open("/littlefs/dir1/dir2/test.txt", O_DIRECTORY | O_NOFOLLOW);
TEST_ASSERT_EQUAL(-1, fd);
TEST_ASSERT_EQUAL(ENOTDIR, errno);
test_teardown();
}
#endif
TEST_CASE("fcntl get flags", "[littlefs]")
{
int fd;
int ret;
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
char path[MAXPATHLEN];
#endif
test_setup();
fd = open("/littlefs/test.txt", O_CREAT | O_WRONLY);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
ret = fcntl(fd, F_GETFL);
TEST_ASSERT_EQUAL(O_WRONLY, ret);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/test.txt", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
fd = open("/littlefs/test.txt", O_RDONLY);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
ret = fcntl(fd, F_GETFL);
TEST_ASSERT_EQUAL(O_RDONLY, ret);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/test.txt", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
fd = open("/littlefs/test.txt", O_RDWR);
TEST_ASSERT_GREATER_OR_EQUAL_INT(0, fd);
ret = fcntl(fd, F_GETFL);
TEST_ASSERT_EQUAL(O_RDWR, ret);
#if CONFIG_LITTLEFS_FCNTL_GET_PATH
ret = fcntl(fd, F_GETPATH, path);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL_STRING("/littlefs/test.txt", path);
memset(path, 0, MAXPATHLEN);
#endif
ret = close(fd);
TEST_ASSERT_EQUAL(0, ret);
test_teardown();
}
/**
* Cannot use buitin `stat` since it depends on CONFIG_VFS_SUPPORT_DIR.
*/
static int test_littlefs_stat(const char *path, struct stat *buf){
int res;
FILE* f = fopen(path, "r");
if(!f){
return -1;
}
int fd = fileno(f);
res = fstat(fd, buf);
fclose(f);
return res;
}