littlefs/tests/test_compat.toml

1454 lines
42 KiB
C

# Test for compatibility between different littlefs versions
#
# Note, these tests are a bit special. They expect to be linked against two
# different versions of littlefs:
# - lfs => the new/current version of littlefs
# - lfsp => the previous version of littlefs
#
# If lfsp is not linked, and LFSP is not defined, these tests will alias
# the relevant lfs types/functions as necessary so at least the tests can
# themselves be tested locally.
#
# But to get value from these tests, it's expected that the previous version
# of littlefs be linked in during CI, with the help of scripts/changeprefix.py
#
# alias littlefs symbols as needed
#
# there may be a better way to do this, but oh well, explicit aliases works
code = '''
#ifdef LFSP
#define STRINGIZE(x) STRINGIZE_(x)
#define STRINGIZE_(x) #x
#include STRINGIZE(LFSP)
#else
#define LFSP_DISK_VERSION LFS_DISK_VERSION
#define LFSP_DISK_VERSION_MAJOR LFS_DISK_VERSION_MAJOR
#define LFSP_DISK_VERSION_MINOR LFS_DISK_VERSION_MINOR
#define lfsp_t lfs_t
#define lfsp_config lfs_config
#define lfsp_format lfs_format
#define lfsp_mount lfs_mount
#define lfsp_unmount lfs_unmount
#define lfsp_fsinfo lfs_fsinfo
#define lfsp_fs_stat lfs_fs_stat
#define lfsp_dir_t lfs_dir_t
#define lfsp_info lfs_info
#define LFSP_TYPE_REG LFS_TYPE_REG
#define LFSP_TYPE_DIR LFS_TYPE_DIR
#define lfsp_mkdir lfs_mkdir
#define lfsp_dir_open lfs_dir_open
#define lfsp_dir_read lfs_dir_read
#define lfsp_dir_close lfs_dir_close
#define lfsp_file_t lfs_file_t
#define LFSP_O_RDONLY LFS_O_RDONLY
#define LFSP_O_WRONLY LFS_O_WRONLY
#define LFSP_O_CREAT LFS_O_CREAT
#define LFSP_O_EXCL LFS_O_EXCL
#define LFSP_SEEK_SET LFS_SEEK_SET
#define lfsp_file_open lfs_file_open
#define lfsp_file_write lfs_file_write
#define lfsp_file_read lfs_file_read
#define lfsp_file_seek lfs_file_seek
#define lfsp_file_close lfs_file_close
#endif
'''
## forward-compatibility tests ##
# test we can mount in a new version
[cases.test_compat_forward_mount]
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// confirm the previous mount works
lfsp_mount(&lfsp, &cfgp) => 0;
lfsp_unmount(&lfsp) => 0;
// now test the new mount
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
lfs_unmount(&lfs) => 0;
'''
# test we can read dirs in a new version
[cases.test_compat_forward_read_dirs]
defines.COUNT = 5
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT dirs
lfsp_mount(&lfsp, &cfgp) => 0;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
'''
# test we can read files in a new version
[cases.test_compat_forward_read_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the files?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can read files in dirs in a new version
[cases.test_compat_forward_read_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT files+dirs
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
lfsp_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, name) => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can write dirs in a new version
[cases.test_compat_forward_write_dirs]
defines.COUNT = 10
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write COUNT/2 dirs
lfsp_mount(&lfsp, &cfgp) => 0;
for (lfs_size_t i = 0; i < COUNT/2; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write another COUNT/2 dirs
for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0;
'''
# test we can write files in a new version
[cases.test_compat_forward_write_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write half COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// write half
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
// can we list the files?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
# test we can write files in dirs in a new version
[cases.test_compat_forward_write_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_format(&lfsp, &cfgp) => 0;
// write half COUNT files
lfsp_mount(&lfsp, &cfgp) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
// write half
lfsp_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name,
LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfsp_unmount(&lfsp) => 0;
// mount the new version
lfs_t lfs;
lfs_mount(&lfs, cfg) => 0;
// we should be able to read the version using lfs_fs_stat
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFSP_DISK_VERSION);
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
// can we list the directories?
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, name) => 0;
struct lfs_info info;
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfs_dir_read(&lfs, &dir, &info) => 1;
assert(info.type == LFS_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
'''
## backwards-compatibility tests ##
# test we can mount in an old version
[cases.test_compat_backward_mount]
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// confirm the new mount works
lfs_mount(&lfs, cfg) => 0;
lfs_unmount(&lfs) => 0;
// now test the previous mount
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can read dirs in an old version
[cases.test_compat_backward_read_dirs]
defines.COUNT = 5
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT dirs
lfs_mount(&lfs, cfg) => 0;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can read files in an old version
[cases.test_compat_backward_read_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the files?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can read files in dirs in an old version
[cases.test_compat_backward_read_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 4
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT files+dirs
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
lfs_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, name) => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can write dirs in an old version
[cases.test_compat_backward_write_dirs]
defines.COUNT = 10
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the new version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write COUNT/2 dirs
lfs_mount(&lfs, cfg) => 0;
for (lfs_size_t i = 0; i < COUNT/2; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
}
lfs_unmount(&lfs) => 0;
// mount the previous version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write another COUNT/2 dirs
for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_mkdir(&lfsp, name) => 0;
}
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
lfsp_unmount(&lfsp) => 0;
'''
# test we can write files in an old version
[cases.test_compat_backward_write_files]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write half COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// write half
lfs_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
// can we list the files?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
char name[8];
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[8];
sprintf(name, "file%03d", i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
# test we can write files in dirs in an old version
[cases.test_compat_backward_write_files_in_dirs]
defines.COUNT = 5
defines.SIZE = [4, 32, 512, 8192]
defines.CHUNK = 2
if = '''
LFS_DISK_VERSION == LFSP_DISK_VERSION
&& DISK_VERSION == 0
'''
code = '''
// create the previous version
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// write half COUNT files
lfs_mount(&lfs, cfg) => 0;
uint32_t prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[16];
sprintf(name, "dir%03d", i);
lfs_mkdir(&lfs, name) => 0;
// write half
lfs_file_t file;
sprintf(name, "dir%03d/file%03d", i, i);
lfs_file_open(&lfs, &file, name,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
}
lfs_file_close(&lfs, &file) => 0;
// skip the other half but keep our prng reproducible
for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
TEST_PRNG(&prng);
}
}
lfs_unmount(&lfs) => 0;
// mount the new version
struct lfsp_config cfgp;
memcpy(&cfgp, cfg, sizeof(cfgp));
lfsp_t lfsp;
lfsp_mount(&lfsp, &cfgp) => 0;
// write half COUNT files
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
// skip half but keep our prng reproducible
for (lfs_size_t j = 0; j < SIZE/2; j++) {
TEST_PRNG(&prng);
}
// write the other half
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
for (lfs_size_t k = 0; k < CHUNK; k++) {
chunk[k] = TEST_PRNG(&prng) & 0xff;
}
lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
}
lfsp_file_close(&lfsp, &file) => 0;
}
// can we list the directories?
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, "/") => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
char name[8];
sprintf(name, "dir%03d", i);
assert(strcmp(info.name, name) == 0);
}
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
// can we list the files?
for (lfs_size_t i = 0; i < COUNT; i++) {
char name[8];
sprintf(name, "dir%03d", i);
lfsp_dir_t dir;
lfsp_dir_open(&lfsp, &dir, name) => 0;
struct lfsp_info info;
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, ".") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_DIR);
assert(strcmp(info.name, "..") == 0);
lfsp_dir_read(&lfsp, &dir, &info) => 1;
assert(info.type == LFSP_TYPE_REG);
sprintf(name, "file%03d", i);
assert(strcmp(info.name, name) == 0);
assert(info.size == SIZE);
lfsp_dir_read(&lfsp, &dir, &info) => 0;
lfsp_dir_close(&lfsp, &dir) => 0;
}
// now can we read the files?
prng = 42;
for (lfs_size_t i = 0; i < COUNT; i++) {
lfsp_file_t file;
char name[16];
sprintf(name, "dir%03d/file%03d", i, i);
lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
uint8_t chunk[CHUNK];
lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
for (lfs_size_t k = 0; k < CHUNK; k++) {
assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
}
}
lfsp_file_close(&lfsp, &file) => 0;
}
lfsp_unmount(&lfsp) => 0;
'''
## incompatiblity tests ##
# test that we fail to mount after a major version bump
[cases.test_compat_major_incompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// bump the major version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION + 0x00010000,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->block_count,
.name_max = lfs.name_max,
.file_max = lfs.file_max,
.attr_max = lfs.attr_max,
};
lfs_superblock_tole32(&superblock);
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should now fail
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
# test that we fail to mount after a minor version bump
[cases.test_compat_minor_incompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// bump the minor version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION + 0x00000001,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->block_count,
.name_max = lfs.name_max,
.file_max = lfs.file_max,
.attr_max = lfs.attr_max,
};
lfs_superblock_tole32(&superblock);
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should now fail
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
# test that we correctly bump the minor version
[cases.test_compat_minor_bump]
in = 'lfs.c'
if = '''
LFS_DISK_VERSION_MINOR > 0
&& DISK_VERSION == 0
'''
code = '''
// create a superblock
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
lfs_file_t file;
lfs_file_open(&lfs, &file, "test",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_write(&lfs, &file, "testtest", 8) => 8;
lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0;
// write an old minor version
//
// note we're messing around with internals to do this! this
// is not a user API
lfs_mount(&lfs, cfg) => 0;
lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_superblock_t superblock = {
.version = LFS_DISK_VERSION - 0x00000001,
.block_size = lfs.cfg->block_size,
.block_count = lfs.cfg->block_count,
.name_max = lfs.name_max,
.file_max = lfs.file_max,
.attr_max = lfs.attr_max,
};
lfs_superblock_tole32(&superblock);
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock})) => 0;
lfs_unmount(&lfs) => 0;
// mount should still work
lfs_mount(&lfs, cfg) => 0;
struct lfs_fsinfo fsinfo;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
uint8_t buffer[8];
lfs_file_read(&lfs, &file, buffer, 8) => 8;
assert(memcmp(buffer, "testtest", 8) == 0);
lfs_file_close(&lfs, &file) => 0;
// minor version should be unchanged
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_unmount(&lfs) => 0;
// if we write, we need to bump the minor version
lfs_mount(&lfs, cfg) => 0;
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
lfs_file_open(&lfs, &file, "test", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_write(&lfs, &file, "teeeeest", 8) => 8;
lfs_file_close(&lfs, &file) => 0;
// minor version should be changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_unmount(&lfs) => 0;
// and of course mount should still work
lfs_mount(&lfs, cfg) => 0;
// minor version should have changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file, buffer, 8) => 8;
assert(memcmp(buffer, "teeeeest", 8) == 0);
lfs_file_close(&lfs, &file) => 0;
// yep, still changed
lfs_fs_stat(&lfs, &fsinfo) => 0;
assert(fsinfo.disk_version == LFS_DISK_VERSION);
lfs_unmount(&lfs) => 0;
'''