blob: 02b404a09aff7b3659e90f40fbf125f3546e83f7 [file] [log] [blame]
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* @filesystem
* @brief test_filesystem
* Tests the fs_open flags
*/
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/fs/fs.h>
#include <string.h>
/* Path for testr file should be provided by test runner and should start
* with mount point.
*/
extern char *test_fs_open_flags_file_path;
static const char something[] = "Something";
static char buffer[sizeof(something)];
#define RDWR_SIZE sizeof(something)
struct test_state {
/* Path to file */
char *file_path;
struct fs_file_t file;
/* Read write buffer info */
const char *write;
int write_size;
char *read;
int read_size;
};
/* ZEQ decides whether test completed successfully and prints appropriate
* information.
*/
static void ZEQ(int ret, int expected)
{
zassert_equal(ret, expected,
"FAILED: expected = %d, ret = %d, errno = %d\n",
expected, ret, errno);
TC_PRINT("SUCCESS\n");
}
/* NOTE: Below functions have C preprocessor redefinitions that automatically
* fill in the line parameter, so when invoking, do not provide the line
* parameter.
*/
/* Test fs_open, expected is the return value expected from fs_open */
static void ZOPEN(struct test_state *ts, int flags, int expected, int line)
{
TC_PRINT("# %d: OPEN %s with flags %x\n", line, ts->file_path, flags);
ZEQ(fs_open(&ts->file, ts->file_path, flags), expected);
}
/* Close file. Will automatically fail test case if unsuccessful. */
static void ZCLOSE(struct test_state *ts, int line)
{
TC_PRINT("# %d: CLOSE %s\n", line, ts->file_path);
ZEQ(fs_close(&ts->file), 0);
}
/* Attempt to write to file; expected is what fs_write is supposed to return */
static void ZWRITE(struct test_state *ts, int expected, int line)
{
TC_PRINT("# %d: WRITE %s\n", line, ts->file_path);
ZEQ(fs_write(&ts->file, ts->write, ts->write_size), expected);
}
/* Attempt to read from file; expected is what fs_read is supposed to return */
static void ZREAD(struct test_state *ts, int expected, int line)
{
TC_PRINT("# %d: READ %s\n", line, ts->file_path);
ZEQ(fs_read(&ts->file, ts->read, ts->read_size), expected);
}
/* Unlink/delete file. Will automatically fail test case if unsuccessful. */
static void ZUNLINK(struct test_state *ts, int line)
{
int ret;
TC_PRINT("# %d: UNLINK %s\n", line, ts->file_path);
ret = fs_unlink(ts->file_path);
zassert((ret == 0 || ret == -ENOENT), "Done", "Failed");
TC_PRINT("SUCCESS\n");
}
/* Check file position; expected is a position file should be at. */
static void ZCHKPOS(struct test_state *ts, off_t expected, int line)
{
TC_PRINT("# %d: CHKPOS\n", line);
ZEQ(fs_tell(&ts->file), expected);
}
/* Rewind file. */
static void ZREWIND(struct test_state *ts, int line)
{
TC_PRINT("# %d: REWIND\n", line);
ZEQ(fs_seek(&ts->file, 0, FS_SEEK_SET), 0);
}
/* Banner definitions, print BEGIN/END banner with block number.
* Require definition of block variable with vale that will represent first
* test block number; the variable is automatically incremented by END.
*/
#define ZBEGIN(info) \
TC_PRINT("\n## BEGIN %d: %s (line %d)\n", block, info, __LINE__)
#define ZEND() TC_PRINT("## END %d\n", block++)
/* C preprocessor redefinitions that automatically fill in line parameter */
#define ZOPEN(ts, flags, expected) ZOPEN(ts, flags, expected, __LINE__)
#define ZCLOSE(ts) ZCLOSE(ts, __LINE__)
#define ZWRITE(ts, expected) ZWRITE(ts, expected, __LINE__)
#define ZREAD(ts, expected) ZREAD(ts, expected, __LINE__)
#define ZUNLINK(ts) ZUNLINK(ts, __LINE__)
#define ZCHKPOS(ts, expected) ZCHKPOS(ts, expected, __LINE__)
#define ZREWIND(ts) ZREWIND(ts, __LINE__)
/* Create empty file */
#define ZMKEMPTY(ts) \
do { \
ZUNLINK(ts); \
ZOPEN(ts, FS_O_CREATE, 0); \
ZCLOSE(ts); \
} while (0)
void test_fs_open_flags(void)
{
struct test_state ts = {
*&test_fs_open_flags_file_path,
{ 0 },
something,
RDWR_SIZE,
buffer,
RDWR_SIZE,
};
int block = 1;
fs_file_t_init(&ts.file);
ZBEGIN("Attempt open non-existent");
ZOPEN(&ts, 0, -ENOENT);
ZOPEN(&ts, FS_O_WRITE, -ENOENT);
ZOPEN(&ts, FS_O_READ, -ENOENT);
ZOPEN(&ts, FS_O_RDWR, -ENOENT);
ZOPEN(&ts, FS_O_APPEND, -ENOENT);
ZOPEN(&ts, FS_O_APPEND | FS_O_READ, -ENOENT);
ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, -ENOENT);
ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR, -ENOENT);
ZEND();
/* Attempt create new file with no read/write access and check
* operations on it.
*/
ZBEGIN("Attempt create new with no R/W access");
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZOPEN(&ts, FS_O_CREATE | 0, 0);
ZWRITE(&ts, -EACCES);
ZREAD(&ts, -EACCES);
ZCLOSE(&ts);
ZUNLINK(&ts);
#else
TC_PRINT("Bypassed test\n");
#endif
ZEND();
ZBEGIN("Attempt create new with READ access");
ZOPEN(&ts, FS_O_CREATE | FS_O_READ, 0);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZWRITE(&ts, -EACCES);
#else
TC_PRINT("Write bypassed\n");
#endif
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt create new with WRITE access");
ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
ZWRITE(&ts, ts.write_size);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZREAD(&ts, -EACCES);
#else
TC_PRINT("Read bypassed\n");
#endif
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt create new with R/W access");
ZOPEN(&ts, FS_O_CREATE | FS_O_RDWR, 0);
ZWRITE(&ts, ts.write_size);
/* Read is done at the end of file, so 0 bytes will be read */
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt open existing with no R/W access");
ZMKEMPTY(&ts);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
ZOPEN(&ts, 0, 0);
ZWRITE(&ts, -EACCES);
ZREAD(&ts, -EACCES);
ZCLOSE(&ts);
#else
TC_PRINT("Bypassed test\n");
#endif
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt open existing with READ access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_READ, 0);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZWRITE(&ts, -EACCES);
#else
TC_PRINT("Write bypassed\n");
#endif
/* File is empty */
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt open existing with WRITE access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_WRITE, 0);
ZCHKPOS(&ts, 0);
ZWRITE(&ts, ts.write_size);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZREAD(&ts, -EACCES);
#else
TC_PRINT("Read bypassed\n");
#endif
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt open existing with R/W access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_RDWR, 0);
ZWRITE(&ts, ts.write_size);
/* Read is done at the end of file, so 0 bytes will be read */
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt append existing with no R/W access");
ZMKEMPTY(&ts);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
ZOPEN(&ts, FS_O_APPEND, 0);
ZCHKPOS(&ts, 0);
ZWRITE(&ts, -EACCES);
ZREAD(&ts, -EACCES);
ZCLOSE(&ts);
#else
TC_PRINT("Test bypassed\n");
#endif
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt append existing with READ access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_APPEND | FS_O_READ, 0);
ZCHKPOS(&ts, 0);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZWRITE(&ts, -EACCES);
#else
TC_PRINT("Write bypassed\n");
#endif
/* File is empty */
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt append existing with WRITE access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
ZCHKPOS(&ts, 0);
ZWRITE(&ts, ts.write_size);
#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
ZREAD(&ts, -EACCES);
#else
TC_PRINT("Read bypassed\n");
#endif
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Attempt append existing with R/W access");
ZMKEMPTY(&ts);
ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR, 0);
ZCHKPOS(&ts, 0);
ZWRITE(&ts, ts.write_size);
/* Read is done at the end of file, so 0 bytes will be read */
ZREAD(&ts, 0);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
/* This is simple check by file position, not contents. Since writing
* same pattern twice, the position of file should be twice the
* ts.write_size.
*/
ZBEGIN("Check if append adds data to file");
/* Prepare file */
ZUNLINK(&ts);
ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
ZWRITE(&ts, ts.write_size);
ZCLOSE(&ts);
ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
ZCHKPOS(&ts, 0);
ZWRITE(&ts, ts.write_size);
ZCHKPOS(&ts, ts.write_size * 2);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
ZBEGIN("Check if appended forwards file before write");
/* Prepare file */
ZUNLINK(&ts);
ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
ZWRITE(&ts, ts.write_size);
ZCLOSE(&ts);
ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
ZCHKPOS(&ts, 0);
ZREWIND(&ts);
ZWRITE(&ts, ts.write_size);
ZCHKPOS(&ts, ts.write_size * 2);
ZCLOSE(&ts);
ZUNLINK(&ts);
ZEND();
}