test: fs: fat_fs_api: Add tests for reentrant zephyr support
This commit adds tests for the FatFs FF_FS_REENTRANT option. Signed-off-by: Nicola Ochsenbein <Nicola.Ochsenbein@husqvarnagroup.com>
This commit is contained in:
parent
eba73727ee
commit
31a384719d
|
@ -18,3 +18,5 @@ target_sources(app PRIVATE
|
|||
target_sources_ifdef(CONFIG_FLASH app PRIVATE
|
||||
../common/test_fs_mkfs.c
|
||||
src/test_fat_mkfs.c)
|
||||
target_sources_ifdef(CONFIG_FS_FATFS_REENTRANT app PRIVATE
|
||||
src/test_fat_file_reentrant.c)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
* Copyright (c) 2023 Husqvarna AB
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "test_fat.h"
|
||||
|
||||
/* FatFs work area */
|
||||
FATFS fat_fs;
|
||||
struct fs_file_t filep;
|
||||
const char test_str[] = "hello world!";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
* Copyright (c) 2023 Husqvarna AB
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -18,6 +19,9 @@ static void *fat_fs_basic_setup(void)
|
|||
test_fat_fs();
|
||||
test_fat_rename();
|
||||
test_fs_open_flags();
|
||||
#ifdef CONFIG_FS_FATFS_REENTRANT
|
||||
test_fat_file_reentrant();
|
||||
#endif /* CONFIG_FS_FATFS_REENTRANT */
|
||||
test_fat_unmount();
|
||||
|
||||
return NULL;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2023 Husqvarna AB
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -8,6 +9,7 @@
|
|||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/ztest.h>
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <ff.h>
|
||||
|
||||
#ifdef CONFIG_DISK_DRIVER_RAM
|
||||
#define DISK_NAME CONFIG_DISK_RAM_VOLUME_NAME
|
||||
|
@ -27,12 +29,14 @@
|
|||
"/testlongfilenamethatsmuchlongerthan8.3chars.text"
|
||||
#else
|
||||
#define TEST_FILE FATFS_MNTP"/testfile.txt"
|
||||
#endif /* IS_ENABLED(CONFIG_FS_FATFS_LFN) */
|
||||
#endif /* CONFIG_FS_FATFS_LFN */
|
||||
#define TEST_DIR FATFS_MNTP"/testdir"
|
||||
#define TEST_DIR_FILE FATFS_MNTP"/testdir/testfile.txt"
|
||||
|
||||
extern struct fs_file_t filep;
|
||||
extern const char test_str[];
|
||||
/* FatFs work area */
|
||||
extern FATFS fat_fs;
|
||||
|
||||
int check_file_dir_exists(const char *path);
|
||||
|
||||
|
@ -42,3 +46,6 @@ void test_fat_file(void);
|
|||
void test_fat_dir(void);
|
||||
void test_fat_fs(void);
|
||||
void test_fat_rename(void);
|
||||
#ifdef CONFIG_FS_FATFS_REENTRANT
|
||||
void test_fat_file_reentrant(void);
|
||||
#endif /* CONFIG_FS_FATFS_REENTRANT */
|
||||
|
|
179
tests/subsys/fs/fat_fs_api/src/test_fat_file_reentrant.c
Normal file
179
tests/subsys/fs/fat_fs_api/src/test_fat_file_reentrant.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Husqvarna AB
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "test_fat.h"
|
||||
|
||||
#ifdef CONFIG_FS_FATFS_REENTRANT
|
||||
|
||||
#define REENTRANT_TEST_STACK_SIZE 500
|
||||
#define SEMAPHORE_OP_SUCCESS 0
|
||||
#define TEST_FILE2 FATFS_MNTP"/tfile2.txt"
|
||||
|
||||
void tlock_mutex(void *p1, void *p2, void *p3);
|
||||
void tfile2_access(void *p1, void *p2, void *p3);
|
||||
|
||||
K_THREAD_STACK_DEFINE(tlock_mutex_stack_area, REENTRANT_TEST_STACK_SIZE);
|
||||
K_THREAD_STACK_DEFINE(tfile2_access_stack_area, REENTRANT_TEST_STACK_SIZE);
|
||||
struct k_thread tlock_mutex_data;
|
||||
struct k_thread tfile2_access_data;
|
||||
struct k_sem mutex_unlocked_sem;
|
||||
struct k_sem run_non_thread_sem;
|
||||
|
||||
static int test_reentrant_access(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
TC_PRINT("\nReentrant tests:\n");
|
||||
zassert_ok(k_sem_init(&mutex_unlocked_sem, 0, 1), NULL);
|
||||
zassert_ok(k_sem_init(&run_non_thread_sem, 0, 1), NULL);
|
||||
|
||||
/* Start mutex locking thread */
|
||||
k_tid_t tid = k_thread_create(&tlock_mutex_data, tlock_mutex_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(tlock_mutex_stack_area),
|
||||
tlock_mutex,
|
||||
NULL, NULL, NULL,
|
||||
K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
|
||||
|
||||
/* Make sure thread was able to lock mutex */
|
||||
k_sem_take(&run_non_thread_sem, K_FOREVER);
|
||||
|
||||
/* File open should wait here, as the fs is locked. Therefore, automatic switch back to
|
||||
* thread
|
||||
*/
|
||||
TC_PRINT("Open file\n");
|
||||
res = fs_open(&filep, TEST_FILE, FS_O_CREATE | FS_O_RDWR);
|
||||
zassert_ok(res, "Err: File could not be opened [%d]\n", res);
|
||||
TC_PRINT("File opened\n");
|
||||
|
||||
/* Check if mutex thread really unlocked the mutexes */
|
||||
zassert_equal(SEMAPHORE_OP_SUCCESS, k_sem_take(&mutex_unlocked_sem, K_NO_WAIT),
|
||||
"File open with locked mutex");
|
||||
|
||||
/* Cleanup */
|
||||
res = fs_close(&filep);
|
||||
zassert_ok(res, "Error closing file [%d]\n", res);
|
||||
res = fs_unlink(TEST_FILE);
|
||||
zassert_ok(res, "Error deleting file [%d]\n", res);
|
||||
|
||||
k_thread_join(tid, K_FOREVER);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_reentrant_parallel_file_access(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
TC_PRINT("\nParallel reentrant-safe file access test:\n");
|
||||
|
||||
TC_PRINT("Open file 1\n");
|
||||
res = fs_open(&filep, TEST_FILE, FS_O_CREATE | FS_O_RDWR);
|
||||
zassert_ok(res, "Err: File 1 could not be opened [%d]\n", res);
|
||||
TC_PRINT("File 1 opened 1\n");
|
||||
|
||||
/* Start 2nd file acces thread */
|
||||
k_tid_t tid = k_thread_create(&tfile2_access_data, tfile2_access_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(tfile2_access_stack_area),
|
||||
tfile2_access,
|
||||
NULL, NULL, NULL,
|
||||
K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
|
||||
|
||||
/* Wait for thread to finish accessing file 2 */
|
||||
k_thread_join(tid, K_FOREVER);
|
||||
|
||||
/* Check existence of file 2 */
|
||||
struct fs_file_t filep2;
|
||||
|
||||
fs_file_t_init(&filep2);
|
||||
|
||||
TC_PRINT("Check file 2 existence\n");
|
||||
res = fs_open(&filep2, TEST_FILE2, FA_OPEN_EXISTING | FA_READ);
|
||||
zassert_ok(res, "Err: File 2 does not exist [%d]\n", res);
|
||||
|
||||
/* Cleanup */
|
||||
res = fs_close(&filep2);
|
||||
zassert_ok(res, "Error closing file 2 [%d]\n", res);
|
||||
res = fs_unlink(TEST_FILE2);
|
||||
zassert_ok(res, "Error deleting file 2 [%d]\n", res);
|
||||
res = fs_close(&filep);
|
||||
zassert_ok(res, "Error closing file 1 [%d]\n", res);
|
||||
res = fs_unlink(TEST_FILE);
|
||||
zassert_ok(res, "Error deleting file 1 [%d]\n", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void release_dirty_mutex(void)
|
||||
{
|
||||
ff_mutex_give(fat_fs.ldrv);
|
||||
}
|
||||
|
||||
int request_dirty_mutex(void)
|
||||
{
|
||||
return ff_mutex_take(fat_fs.ldrv);
|
||||
}
|
||||
|
||||
void tlock_mutex(void *p1, void *p2, void *p3)
|
||||
{
|
||||
TC_PRINT("Mutex thread: Started, locking fs\n");
|
||||
request_dirty_mutex();
|
||||
TC_PRINT("Mutex thread: Lock acquired, yield to switch back to try to open file\n");
|
||||
k_sem_give(&run_non_thread_sem);
|
||||
k_yield();
|
||||
|
||||
TC_PRINT("Mutex thread: Got back to thread, release mutex now and give semaphore to check "
|
||||
"if file opened\n");
|
||||
k_sem_give(&mutex_unlocked_sem);
|
||||
release_dirty_mutex();
|
||||
|
||||
TC_PRINT("Mutex thread: Lock released, thread terminating\n");
|
||||
}
|
||||
|
||||
void tfile2_access(void *p1, void *p2, void *p3)
|
||||
{
|
||||
int res;
|
||||
ssize_t brw;
|
||||
struct fs_file_t filep2;
|
||||
|
||||
TC_PRINT("File 2 access thread started\n");
|
||||
|
||||
/* Init fp for 2nd File for parallel access test */
|
||||
fs_file_t_init(&filep2);
|
||||
|
||||
/* open 2nd file */
|
||||
TC_PRINT("Open file 2\n");
|
||||
res = fs_open(&filep2, TEST_FILE2, FS_O_CREATE | FS_O_RDWR);
|
||||
zassert_ok(res, "Err: File 2 could not be opened [%d]\n", res);
|
||||
TC_PRINT("File 2 opened 2\n");
|
||||
|
||||
/* Verify fs_write() not locked */
|
||||
brw = fs_write(&filep2, (char *)test_str, strlen(test_str));
|
||||
if (brw < 0) {
|
||||
TC_PRINT("Failed writing to file [%zd]\n", brw);
|
||||
fs_close(&filep2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (brw < strlen(test_str)) {
|
||||
TC_PRINT("Unable to complete write. Volume full.\n");
|
||||
TC_PRINT("Number of bytes written: [%zd]\n", brw);
|
||||
fs_close(&filep2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Close file and switch back to test context*/
|
||||
res = fs_close(&filep2);
|
||||
zassert_ok(res, "Error closing file [%d]\n", res);
|
||||
|
||||
TC_PRINT("File 2 access thread successfully wrote to file 2\n");
|
||||
}
|
||||
|
||||
void test_fat_file_reentrant(void)
|
||||
{
|
||||
zassert_true(test_reentrant_access() == TC_PASS, NULL);
|
||||
zassert_true(test_reentrant_parallel_file_access() == TC_PASS, NULL);
|
||||
}
|
||||
#endif /* CONFIG_FS_FATFS_REENTRANT */
|
|
@ -13,9 +13,6 @@
|
|||
#include <zephyr/storage/disk_access.h>
|
||||
#endif
|
||||
|
||||
/* FatFs work area */
|
||||
static FATFS fat_fs;
|
||||
|
||||
/* mounting info */
|
||||
static struct fs_mount_t fatfs_mnt = {
|
||||
.type = FS_FATFS,
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
|
||||
|
||||
#include "test_fat.h"
|
||||
#include <ff.h>
|
||||
|
||||
/* FatFs work area */
|
||||
static FATFS fat_fs;
|
||||
|
||||
/* mounting info */
|
||||
static struct fs_mount_t fatfs_mnt = {
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
|
||||
#include "test_fat.h"
|
||||
#include <ff.h>
|
||||
|
||||
/* FatFs work area */
|
||||
static FATFS fat_fs;
|
||||
|
||||
/* mounting info */
|
||||
static struct fs_mount_t fatfs_mnt = {
|
||||
|
|
|
@ -14,3 +14,8 @@ tests:
|
|||
filesystem.fat.ram.api:
|
||||
platform_allow: native_posix
|
||||
extra_args: CONF_FILE="prj_native_posix_ram.conf"
|
||||
filesystem.fat.api.reentrant:
|
||||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_FS_FATFS_REENTRANT=y
|
||||
- CONFIG_MULTITHREADING=y
|
||||
|
|
Loading…
Reference in a new issue