diff --git a/lfs.c b/lfs.c index 578896db..0fedf191 100644 --- a/lfs.c +++ b/lfs.c @@ -1562,7 +1562,7 @@ static inline lfsr_attr_t lfsr_attr( LFSR_ATTR_CAT_( \ _tag, \ _weight, \ - (const lfsr_data_t[]){__VA_ARGS__}, \ + ((const lfsr_data_t[]){__VA_ARGS__}), \ sizeof((const lfsr_data_t[]){__VA_ARGS__}) / sizeof(lfsr_data_t)) #define LFSR_ATTR_NOOP() \ diff --git a/tests/test_compat.toml b/tests/test_compat.toml index 2e5db317..7604a4cf 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -4,6 +4,7 @@ after = [ 'test_files', 'test_fwrite', 'test_forphans', + 'test_incompat', ] # Note, these tests are a bit special. They can run as-is to test various @@ -1372,135 +1373,3 @@ code = ''' 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; - 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_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; - 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_INVAL; -''' - -# test that we fail to mount after incompatible rcompat flags -[cases.test_compat_rcompat_incompat] -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_INVAL; -''' - -# test that we fail to mount after incompatible wcompat flags -[cases.test_compat_wcompat_incompat] -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_INVAL; -''' - -# test that an incompatible ocompat flag is a noop -[cases.test_compat_ocompat_incompat] -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; -''' diff --git a/tests/test_incompat.toml b/tests/test_incompat.toml new file mode 100644 index 00000000..dc862e59 --- /dev/null +++ b/tests/test_incompat.toml @@ -0,0 +1,318 @@ +# Test (in)compatibility-related things +# +# Unlike test_compat, this focuses on specific corner-cases +# +after = [ + 'test_dirs', + 'test_files', + 'test_fwrite', + 'test_forphans', +] + + +## incompatiblity tests ## + +# 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_INVAL; +''' + +# 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_INVAL; +''' + +# 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_INVAL; +''' + +# 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_INVAL; +''' + +# 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_INVAL; +''' + +[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_INVAL; +''' + +[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_INVAL; +''' + +# 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_INVAL; +''' + +# 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_INVAL; +''' + +# 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_INVAL; +''' +