blob: 80448267ec3467710e408adaf30b0a58dc38fc6f [file] [log] [blame]
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Basic littlefs operations:
* * create
* * write
* * stat
* * read
* * seek
* * tell
* * truncate
* * unlink
* * sync
*/
#include <string.h>
#include <ztest.h>
#include "testfs_tests.h"
#include "testfs_lfs.h"
#include <lfs.h>
#include <fs/littlefs.h>
#define HELLO "hello"
#define GOODBYE "goodbye"
static int mount(struct fs_mount_t *mp)
{
TC_PRINT("mounting %s\n", mp->mnt_point);
zassert_equal(fs_mount(mp), 0,
"mount failed");
return TC_PASS;
}
static int clear_partition(struct fs_mount_t *mp)
{
TC_PRINT("clearing partition %s\n", mp->mnt_point);
zassert_equal(testfs_lfs_wipe_partition(mp),
TC_PASS,
"failed to wipe partition");
return TC_PASS;
}
static int clean_statvfs(const struct fs_mount_t *mp)
{
struct fs_statvfs stat;
TC_PRINT("checking clean statvfs of %s\n", mp->mnt_point);
zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
"statvfs failed");
TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
mp->mnt_point,
stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
zassert_equal(stat.f_bsize, 16,
"bsize fail");
zassert_equal(stat.f_frsize, 4096,
"frsize fail");
zassert_equal(stat.f_blocks, 16,
"blocks fail");
zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
"bfree fail");
return TC_PASS;
}
static int create_write_hello(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("creating and writing file\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
HELLO,
TESTFS_PATH_END)),
0,
"open hello failed");
struct fs_dirent stat;
zassert_equal(fs_stat(path.path, &stat),
0,
"stat new hello failed");
zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
"stat new hello not file");
zassert_equal(strcmp(stat.name, HELLO), 0,
"stat new hello not hello");
zassert_equal(stat.size, 0,
"stat new hello not empty");
zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
TESTFS_BUFFER_SIZE,
"write constant failed");
zassert_equal(fs_stat(path.path, &stat),
0,
"stat written hello failed");
zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
"stat written hello not file");
zassert_equal(strcmp(stat.name, HELLO), 0,
"stat written hello not hello");
/* Anomalous behavior requiring upstream response */
if (true) {
/* VARIATION POINT: littlefs does not update the file size of
* an open file. See upstream issue #250.
*/
zassert_equal(stat.size, 0,
"stat written hello bad size");
}
zassert_equal(fs_close(&file), 0,
"close hello failed");
zassert_equal(fs_stat(path.path, &stat),
0,
"stat closed hello failed");
zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
"stat closed hello not file");
zassert_equal(strcmp(stat.name, HELLO), 0,
"stat closed hello not hello");
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat closed hello badsize");
return TC_PASS;
}
static int verify_hello(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("opening and verifying file\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
HELLO,
TESTFS_PATH_END)),
0,
"verify hello open failed");
zassert_equal(fs_tell(&file), 0U,
"verify hello open tell failed");
zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
TESTFS_BUFFER_SIZE,
"verify hello at start failed");
zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
"verify hello read tell failed");
zassert_equal(fs_close(&file), 0,
"verify close hello failed");
return TC_PASS;
}
static int seek_within_hello(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("seek and tell in file\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
HELLO,
TESTFS_PATH_END)),
0,
"verify hello open failed");
zassert_equal(fs_tell(&file), 0U,
"verify hello open tell failed");
struct fs_dirent stat;
zassert_equal(fs_stat(path.path, &stat),
0,
"stat old hello failed");
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat old hello bad size");
off_t pos = stat.size / 4;
zassert_equal(fs_seek(&file, pos, FS_SEEK_SET),
0,
"verify hello seek near mid failed");
zassert_equal(fs_tell(&file), pos,
"verify hello tell near mid failed");
zassert_equal(testfs_verify_incrementing(&file, pos, TESTFS_BUFFER_SIZE),
TESTFS_BUFFER_SIZE - pos,
"verify hello at middle failed");
zassert_equal(fs_tell(&file), stat.size,
"verify hello read middle tell failed");
zassert_equal(fs_seek(&file, -stat.size, FS_SEEK_CUR),
0,
"verify hello seek back from cur failed");
zassert_equal(fs_tell(&file), 0U,
"verify hello tell back from cur failed");
zassert_equal(fs_seek(&file, -pos, FS_SEEK_END),
0,
"verify hello seek from end failed");
zassert_equal(fs_tell(&file), stat.size - pos,
"verify hello tell from end failed");
zassert_equal(testfs_verify_incrementing(&file, stat.size - pos,
TESTFS_BUFFER_SIZE),
pos,
"verify hello at post middle failed");
zassert_equal(fs_close(&file), 0,
"verify close hello failed");
return TC_PASS;
}
static int truncate_hello(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("truncate in file\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
HELLO,
TESTFS_PATH_END)),
0,
"verify hello open failed");
struct fs_dirent stat;
zassert_equal(fs_stat(path.path, &stat),
0,
"stat old hello failed");
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat old hello bad size");
off_t pos = 3 * stat.size / 4;
zassert_equal(fs_tell(&file), 0U,
"truncate initial tell failed");
zassert_equal(fs_truncate(&file, pos),
0,
"truncate 3/4 failed");
zassert_equal(fs_tell(&file), 0U,
"truncate post tell failed");
zassert_equal(fs_stat(path.path, &stat),
0,
"stat open 3/4 failed");
/* Anomalous behavior requiring upstream response */
if (true) {
/* VARIATION POINT: littlefs does not update the file size of
* an open file. See upstream issue #250.
*/
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat open 3/4 bad size");
}
zassert_equal(testfs_verify_incrementing(&file, 0, 64),
48,
"post truncate content unexpected");
zassert_equal(fs_close(&file), 0,
"post truncate close failed");
/* After close size is correct. */
zassert_equal(fs_stat(path.path, &stat),
0,
"stat closed truncated failed");
zassert_equal(stat.size, pos,
"stat closed truncated bad size");
return TC_PASS;
}
static int unlink_hello(const struct fs_mount_t *mp)
{
struct testfs_path path;
TC_PRINT("unlink hello\n");
testfs_path_init(&path, mp,
HELLO,
TESTFS_PATH_END);
struct fs_dirent stat;
zassert_equal(fs_stat(path.path, &stat),
0,
"stat existing hello failed");
zassert_equal(fs_unlink(path.path),
0,
"unlink hello failed");
zassert_equal(fs_stat(path.path, &stat),
-ENOENT,
"stat existing hello failed");
return TC_PASS;
}
static int sync_goodbye(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("sync goodbye\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
GOODBYE,
TESTFS_PATH_END)),
0,
"sync goodbye failed");
struct fs_dirent stat;
zassert_equal(fs_stat(path.path, &stat),
0,
"stat existing hello failed");
zassert_equal(stat.size, 0,
"stat new goodbye not empty");
zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
TESTFS_BUFFER_SIZE,
"write goodbye failed");
zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
"tell goodbye failed");
if (true) {
/* Upstream issue #250 */
zassert_equal(stat.size, 0,
"stat new goodbye not empty");
}
zassert_equal(fs_sync(&file), 0,
"sync goodbye failed");
zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
"tell synced moved");
zassert_equal(fs_stat(path.path, &stat),
0,
"stat existing hello failed");
printk("sync size %u\n", (u32_t)stat.size);
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat synced goodbye not correct");
zassert_equal(fs_close(&file), 0,
"post sync close failed");
/* After close size is correct. */
zassert_equal(fs_stat(path.path, &stat),
0,
"stat sync failed");
zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
"stat sync bad size");
return TC_PASS;
}
static int verify_goodbye(const struct fs_mount_t *mp)
{
struct testfs_path path;
struct fs_file_t file;
TC_PRINT("verify goodbye\n");
zassert_equal(fs_open(&file,
testfs_path_init(&path, mp,
GOODBYE,
TESTFS_PATH_END)),
0,
"verify goodbye failed");
zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
TESTFS_BUFFER_SIZE,
"write goodbye failed");
zassert_equal(fs_close(&file), 0,
"post sync close failed");
return TC_PASS;
}
static int check_medium(void)
{
struct fs_mount_t *mp = &testfs_medium_mnt;
struct fs_statvfs stat;
zassert_equal(clear_partition(mp), TC_PASS,
"clear partition failed");
zassert_equal(fs_mount(mp), 0,
"medium mount failed");
zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
"statvfs failed");
TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
mp->mnt_point,
stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
zassert_equal(stat.f_bsize, MEDIUM_IO_SIZE,
"bsize fail");
zassert_equal(stat.f_frsize, 4096,
"frsize fail");
zassert_equal(stat.f_blocks, 240,
"blocks fail");
zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
"bfree fail");
zassert_equal(fs_unmount(mp), 0,
"medium unmount failed");
return TC_PASS;
}
static int check_large(void)
{
struct fs_mount_t *mp = &testfs_large_mnt;
struct fs_statvfs stat;
zassert_equal(clear_partition(mp), TC_PASS,
"clear partition failed");
zassert_equal(fs_mount(mp), 0,
"large mount failed");
zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
"statvfs failed");
TC_PRINT("%s: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
mp->mnt_point,
stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
zassert_equal(stat.f_bsize, LARGE_IO_SIZE,
"bsize fail");
zassert_equal(stat.f_frsize, 32768,
"frsize fail");
zassert_equal(stat.f_blocks, 96,
"blocks fail");
zassert_equal(stat.f_bfree, stat.f_blocks - 2U,
"bfree fail");
zassert_equal(fs_unmount(mp), 0,
"large unmount failed");
return TC_PASS;
}
void test_lfs_basic(void)
{
struct fs_mount_t *mp = &testfs_small_mnt;
zassert_equal(clear_partition(mp), TC_PASS,
"clear partition failed");
zassert_equal(mount(mp), TC_PASS,
"clean mount failed");
zassert_equal(clean_statvfs(mp), TC_PASS,
"clean statvfs failed");
zassert_equal(create_write_hello(mp), TC_PASS,
"write hello failed");
zassert_equal(verify_hello(mp), TC_PASS,
"verify hello failed");
zassert_equal(seek_within_hello(mp), TC_PASS,
"seek within hello failed");
zassert_equal(truncate_hello(mp), TC_PASS,
"truncate hello failed");
zassert_equal(unlink_hello(mp), TC_PASS,
"unlink hello failed");
zassert_equal(sync_goodbye(mp), TC_PASS,
"sync goodbye failed");
TC_PRINT("unmounting %s\n", mp->mnt_point);
zassert_equal(fs_unmount(mp), 0,
"unmount small failed");
k_sleep(K_MSEC(100)); /* flush log messages */
TC_PRINT("checking double unmount diagnoses\n");
zassert_equal(fs_unmount(mp), -EINVAL,
"unmount unmounted failed");
zassert_equal(mount(mp), TC_PASS,
"remount failed");
zassert_equal(verify_goodbye(mp), TC_PASS,
"verify goodbye failed");
zassert_equal(fs_unmount(mp), 0,
"unmount2 small failed");
zassert_equal(check_medium(), TC_PASS,
"check medium failed");
zassert_equal(check_large(), TC_PASS,
"check large failed");
}