Files
littlefs/tests/test_incompat.toml
T
Christopher Haster 4a5de70b00 Made mtree implied in lfsr_mtree_* functions, renamed a couple things
This makes mtree implicit in most of littlefs's core functions, which
simplifies things. It also makes lfsr_mtree_traverse naming consistent
with other mtree-esque operation.

Renames:

- Renamed lfsr_fs_weight -> lfsr_mtree_weight (implicit mtree)
- Renamed lfsr_mtree_weight -> lfsr_mtree_weight_ (explicit mtree)
- Renamed lfsr_fs_traverse* -> lfsr_mtree_traverse*
- Renamed LFSR_TSTATE_* -> LFSR_MTRAVERSAL_*

Implicit mtree functions, note these are pretty much the backbone of
littlefs:

- lfsr_mtree_weight
- lfsr_mtree_lookup
- lfsr_mtree_seek
- lfsr_mtree_namelookup
- lfsr_mtree_pathlookup
- lfsr_mtree_traverse

This makes the naming is a bit inconsistent with lfsr_btree_*,
lfsr_rbyd_*, etc, but sometimes rules needs to bend a bit.

Besides, most of these functions needed access to the mroot anyways, so
it's not like they were really ever able to operate on independent
mtrees correctly.

And you can't complain about the code savings:

           code          stack
  before: 34562           2624
  after:  34426 (-0.4%)   2624 (+0.0%)
2024-06-21 11:54:06 -05:00

426 lines
12 KiB
TOML

# Test (in)compatibility-related things
#
# Unlike test_compat, this focuses on specific corner-cases
#
after = ['test_mtree', 'test_dirs', 'test_files']
## incompatiblity tests ##
# test that we fail if we find no magic
[cases.test_incompat_no_magic]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// delete the magic string
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_RM | LFSR_TAG_MAGIC, 0,
LFSR_DATA_NULL()))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_CORRUPT;
'''
# test that we fail if we find bad magic
[cases.test_incompat_bad_magic]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// tweak the magic string
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_MAGIC, 0,
LFSR_DATA_BUF("lottlefs", 8)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_CORRUPT;
'''
# test that we fail to mount after a major version bump
[cases.test_incompat_major]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// bump the major version
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_VERSION, 0,
LFSR_DATA_BUF(((const uint8_t[2]){
LFS_DISK_VERSION_MAJOR+1,
0}), 2)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after a minor version bump
[cases.test_incompat_minor]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// bump the minor version
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_VERSION, 0,
LFSR_DATA_BUF(((const uint8_t[2]){
LFS_DISK_VERSION_MAJOR,
LFS_DISK_VERSION_MINOR+1}), 2)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount incompatible rcompat flags
[cases.test_incompat_rcompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set the nonstandard rcompat flag, this will always be incompatible
// with standard littlefs
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_RCOMPAT, 0,
LFSR_DATA_RCOMPAT(
LFSR_RCOMPAT_COMPAT
| LFSR_RCOMPAT_NONSTANDARD)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount incompatible wcompat flags
[cases.test_incompat_wcompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set the nonstandard rcompat flag, this will always be incompatible
// with standard littlefs
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_WCOMPAT, 0,
LFSR_DATA_WCOMPAT(
LFSR_WCOMPAT_COMPAT
| LFSR_WCOMPAT_NONSTANDARD)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that an incompatible ocompat flag is a noop
[cases.test_incompat_ocompat]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set the nonstandard ocompat flag, this will always be incompatible
// with standard littlefs
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_OCOMPAT, 0,
LFSR_DATA_OCOMPAT(
LFSR_OCOMPAT_COMPAT
| LFSR_OCOMPAT_NONSTANDARD)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should _not_ fail, ocompat should always be ignored
lfsr_mount(&lfs, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
# these are just a bit harder to detect
[cases.test_incompat_rcompat_overflow]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set a really far rcompat flag
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR_CAT(
LFSR_TAG_RCOMPAT, 0,
LFSR_DATA_RCOMPAT(LFSR_RCOMPAT_COMPAT),
LFSR_DATA_BUF("\x00\x00\x00\x00\x80", 5)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
[cases.test_incompat_wcompat_overflow]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set a really far wcompat flag
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR_CAT(
LFSR_TAG_WCOMPAT, 0,
LFSR_DATA_WCOMPAT(LFSR_WCOMPAT_COMPAT),
LFSR_DATA_BUF("\x00\x00\x00\x00\x80", 5)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
[cases.test_incompat_ocompat_overflow]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set a really far ocompat flag
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR_CAT(
LFSR_TAG_OCOMPAT, 0,
LFSR_DATA_OCOMPAT(LFSR_OCOMPAT_COMPAT),
LFSR_DATA_BUF("\x00\x00\x00\x00\x80", 5)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should _not_ fail, ocompat should always be ignored
lfsr_mount(&lfs, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test that we fail to mount incompatible block sizes
[cases.test_incompat_block_size]
defines.INC_BLOCK_SIZE = ['BLOCK_SIZE/2', 'BLOCK_SIZE*2']
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set an incompatible block size
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_GEOMETRY, 0,
LFSR_DATA_GEOMETRY((&(lfsr_geometry_t){
INC_BLOCK_SIZE,
BLOCK_COUNT}))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible block counts
[cases.test_incompat_block_count]
defines.INC_BLOCK_COUNT = ['BLOCK_COUNT*2']
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set an incompatible block count
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_GEOMETRY, 0,
LFSR_DATA_GEOMETRY((&(lfsr_geometry_t){
BLOCK_SIZE,
INC_BLOCK_COUNT}))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible name limit
[cases.test_incompat_name_limit]
defines.INC_NAME_LIMIT = ['LFS_NAME_MAX*2']
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set an incompatible block size
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_NAMELIMIT, 0,
LFSR_DATA_LLEB128(INC_NAME_LIMIT)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible file limit
[cases.test_incompat_file_limit]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// set an incompatible file limit
//
// note we're messing around with internals to do this! this
// is not a user API
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR_CAT(
LFSR_TAG_FILELIMIT, 0,
// it's a bit difficult to test this since file limit
// is usually our integer limit, but we can force a
// larger value by inserting an extra byte into our
// leb128 encoding
LFSR_DATA_BUF("\xff", 1),
LFSR_DATA_LEB128(LFS_FILE_MAX)))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test what happens if we find an unknown config
[cases.test_incompat_unknown_config]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// create an unknown config
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS(
LFSR_ATTR(
LFSR_TAG_CONFIG + 0x13, 0,
LFSR_DATA_BUF("oh no!", strlen("oh no!"))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''
# test what happens if we find an unknown file type
[cases.test_incompat_unknown_type]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
// create some files
lfsr_mount(&lfs, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi c!", strlen("hi c!")) => strlen("hi c!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, CFG) => 0;
lfsr_mdir_t mdir;
lfsr_did_t did;
const char *name;
lfs_size_t name_size;
lfsr_mtree_pathlookup(&lfs, "b",
&mdir, NULL,
&did, &name, &name_size) => LFS_ERR_EXIST;
lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS(
LFSR_ATTR_NAME(
LFSR_TAG_SUB | (LFSR_TAG_NAME + 0x13), 0,
did, name, name_size))) => 0;
lfsr_unmount(&lfs) => 0;
// mount should now fail
lfsr_mount(&lfs, CFG) => LFS_ERR_NOTSUP;
'''