# 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: # - lfs2 => the new/current version of littlefs # - lfs2p => the previous version of littlefs # # If lfs2p is not linked, and LFS2P is not defined, these tests will alias # the relevant lfs2 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 LFS2P #define STRINGIZE(x) STRINGIZE_(x) #define STRINGIZE_(x) #x #include STRINGIZE(LFS2P) #else #define LFS2P_DISK_VERSION LFS2_DISK_VERSION #define LFS2P_DISK_VERSION_MAJOR LFS2_DISK_VERSION_MAJOR #define LFS2P_DISK_VERSION_MINOR LFS2_DISK_VERSION_MINOR #define lfs2p_t lfs2_t #define lfs2p_config lfs2_config #define lfs2p_format lfs2_format #define lfs2p_mount lfs2_mount #define lfs2p_unmount lfs2_unmount #define lfs2p_fsinfo lfs2_fsinfo #define lfs2p_fs_stat lfs2_fs_stat #define lfs2p_dir_t lfs2_dir_t #define lfs2p_info lfs2_info #define LFS2P_TYPE_REG LFS2_TYPE_REG #define LFS2P_TYPE_DIR LFS2_TYPE_DIR #define lfs2p_mkdir lfs2_mkdir #define lfs2p_dir_open lfs2_dir_open #define lfs2p_dir_read lfs2_dir_read #define lfs2p_dir_close lfs2_dir_close #define lfs2p_file_t lfs2_file_t #define LFS2P_O_RDONLY LFS2_O_RDONLY #define LFS2P_O_WRONLY LFS2_O_WRONLY #define LFS2P_O_CREAT LFS2_O_CREAT #define LFS2P_O_EXCL LFS2_O_EXCL #define LFS2P_SEEK_SET LFS2_SEEK_SET #define lfs2p_file_open lfs2_file_open #define lfs2p_file_write lfs2_file_write #define lfs2p_file_read lfs2_file_read #define lfs2p_file_seek lfs2_file_seek #define lfs2p_file_close lfs2_file_close #endif ''' ## forward-compatibility tests ## # test we can mount in a new version [cases.test_compat_forward_mount] if = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // confirm the previous mount works lfs2p_mount(&lfs2p, &cfgp) => 0; lfs2p_unmount(&lfs2p) => 0; // now test the new mount lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); lfs2_unmount(&lfs2) => 0; ''' # test we can read dirs in a new version [cases.test_compat_forward_read_dirs] defines.COUNT = 5 if = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write COUNT dirs lfs2p_mount(&lfs2p, &cfgp) => 0; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2p_mkdir(&lfs2p, name) => 0; } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // can we list the directories? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; lfs2_unmount(&lfs2) => 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 = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write COUNT files lfs2p_mount(&lfs2p, &cfgp) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY | LFS2P_O_CREAT | LFS2P_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // can we list the files? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_REG); char name[8]; sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); } lfs2_dir_read(&lfs2, &dir, &info) => 0; // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2_file_open(&lfs2, &file, name, LFS2_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2_file_read(&lfs2, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 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 = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write COUNT files+dirs lfs2p_mount(&lfs2p, &cfgp) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[16]; sprintf(name, "dir%03d", i); lfs2p_mkdir(&lfs2p, name) => 0; lfs2p_file_t file; sprintf(name, "dir%03d/file%03d", i, i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY | LFS2P_O_CREAT | LFS2P_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // can we list the directories? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; // can we list the files? for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, name) => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_REG); sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; } // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2_file_open(&lfs2, &file, name, LFS2_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2_file_read(&lfs2, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 0; ''' # test we can write dirs in a new version [cases.test_compat_forward_write_dirs] defines.COUNT = 10 if = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write COUNT/2 dirs lfs2p_mount(&lfs2p, &cfgp) => 0; for (lfs2_size_t i = 0; i < COUNT/2; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2p_mkdir(&lfs2p, name) => 0; } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // write another COUNT/2 dirs for (lfs2_size_t i = COUNT/2; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2_mkdir(&lfs2, name) => 0; } // can we list the directories? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; lfs2_unmount(&lfs2) => 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 = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write half COUNT files lfs2p_mount(&lfs2p, &cfgp) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // write half lfs2p_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY | LFS2P_O_CREAT | LFS2P_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE/2; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; // skip the other half but keep our prng reproducible for (lfs2_size_t j = SIZE/2; j < SIZE; j++) { TEST_PRNG(&prng); } } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // write half COUNT files prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // skip half but keep our prng reproducible for (lfs2_size_t j = 0; j < SIZE/2; j++) { TEST_PRNG(&prng); } // write the other half lfs2_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY) => 0; lfs2_file_seek(&lfs2, &file, SIZE/2, LFS2_SEEK_SET) => SIZE/2; for (lfs2_size_t j = SIZE/2; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; } // can we list the files? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_REG); char name[8]; sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); } lfs2_dir_read(&lfs2, &dir, &info) => 0; // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2_file_open(&lfs2, &file, name, LFS2_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2_file_read(&lfs2, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 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 = ''' LFS2_DISK_VERSION_MAJOR == LFS2P_DISK_VERSION_MAJOR && DISK_VERSION == 0 ''' code = ''' // create the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_format(&lfs2p, &cfgp) => 0; // write half COUNT files lfs2p_mount(&lfs2p, &cfgp) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[16]; sprintf(name, "dir%03d", i); lfs2p_mkdir(&lfs2p, name) => 0; // write half lfs2p_file_t file; sprintf(name, "dir%03d/file%03d", i, i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY | LFS2P_O_CREAT | LFS2P_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE/2; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; // skip the other half but keep our prng reproducible for (lfs2_size_t j = SIZE/2; j < SIZE; j++) { TEST_PRNG(&prng); } } lfs2p_unmount(&lfs2p) => 0; // mount the new version lfs2_t lfs2; lfs2_mount(&lfs2, cfg) => 0; // we should be able to read the version using lfs2_fs_stat struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2P_DISK_VERSION); // write half COUNT files prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // skip half but keep our prng reproducible for (lfs2_size_t j = 0; j < SIZE/2; j++) { TEST_PRNG(&prng); } // write the other half lfs2_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY) => 0; lfs2_file_seek(&lfs2, &file, SIZE/2, LFS2_SEEK_SET) => SIZE/2; for (lfs2_size_t j = SIZE/2; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; } // can we list the directories? lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, "/") => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; // can we list the files? for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2_dir_t dir; lfs2_dir_open(&lfs2, &dir, name) => 0; struct lfs2_info info; lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_DIR); assert(strcmp(info.name, "..") == 0); lfs2_dir_read(&lfs2, &dir, &info) => 1; assert(info.type == LFS2_TYPE_REG); sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); lfs2_dir_read(&lfs2, &dir, &info) => 0; lfs2_dir_close(&lfs2, &dir) => 0; } // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2_file_open(&lfs2, &file, name, LFS2_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2_file_read(&lfs2, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 0; ''' ## backwards-compatibility tests ## # test we can mount in an old version [cases.test_compat_backward_mount] if = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the new version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // confirm the new mount works lfs2_mount(&lfs2, cfg) => 0; lfs2_unmount(&lfs2) => 0; // now test the previous mount struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; lfs2p_unmount(&lfs2p) => 0; ''' # test we can read dirs in an old version [cases.test_compat_backward_read_dirs] defines.COUNT = 5 if = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the new version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write COUNT dirs lfs2_mount(&lfs2, cfg) => 0; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2_mkdir(&lfs2, name) => 0; } lfs2_unmount(&lfs2) => 0; // mount the new version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // can we list the directories? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; lfs2p_unmount(&lfs2p) => 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 = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the new version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write COUNT files lfs2_mount(&lfs2, cfg) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 0; // mount the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // can we list the files? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_REG); char name[8]; sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2p_file_read(&lfs2p, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 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 = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the new version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write COUNT files+dirs lfs2_mount(&lfs2, cfg) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[16]; sprintf(name, "dir%03d", i); lfs2_mkdir(&lfs2, name) => 0; lfs2_file_t file; sprintf(name, "dir%03d/file%03d", i, i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; } lfs2_unmount(&lfs2) => 0; // mount the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // can we list the directories? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; // can we list the files? for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, name) => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_REG); sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; } // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2p_file_read(&lfs2p, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 0; ''' # test we can write dirs in an old version [cases.test_compat_backward_write_dirs] defines.COUNT = 10 if = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the new version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write COUNT/2 dirs lfs2_mount(&lfs2, cfg) => 0; for (lfs2_size_t i = 0; i < COUNT/2; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2_mkdir(&lfs2, name) => 0; } lfs2_unmount(&lfs2) => 0; // mount the previous version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // write another COUNT/2 dirs for (lfs2_size_t i = COUNT/2; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2p_mkdir(&lfs2p, name) => 0; } // can we list the directories? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; lfs2p_unmount(&lfs2p) => 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 = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the previous version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write half COUNT files lfs2_mount(&lfs2, cfg) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // write half lfs2_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE/2; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; // skip the other half but keep our prng reproducible for (lfs2_size_t j = SIZE/2; j < SIZE; j++) { TEST_PRNG(&prng); } } lfs2_unmount(&lfs2) => 0; // mount the new version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // write half COUNT files prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // skip half but keep our prng reproducible for (lfs2_size_t j = 0; j < SIZE/2; j++) { TEST_PRNG(&prng); } // write the other half lfs2p_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY) => 0; lfs2p_file_seek(&lfs2p, &file, SIZE/2, LFS2P_SEEK_SET) => SIZE/2; for (lfs2_size_t j = SIZE/2; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; } // can we list the files? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_REG); char name[8]; sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_file_t file; char name[8]; sprintf(name, "file%03d", i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2p_file_read(&lfs2p, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 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 = ''' LFS2_DISK_VERSION == LFS2P_DISK_VERSION && DISK_VERSION == 0 ''' code = ''' // create the previous version lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // write half COUNT files lfs2_mount(&lfs2, cfg) => 0; uint32_t prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { char name[16]; sprintf(name, "dir%03d", i); lfs2_mkdir(&lfs2, name) => 0; // write half lfs2_file_t file; sprintf(name, "dir%03d/file%03d", i, i); lfs2_file_open(&lfs2, &file, name, LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; for (lfs2_size_t j = 0; j < SIZE/2; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2_file_write(&lfs2, &file, chunk, CHUNK) => CHUNK; } lfs2_file_close(&lfs2, &file) => 0; // skip the other half but keep our prng reproducible for (lfs2_size_t j = SIZE/2; j < SIZE; j++) { TEST_PRNG(&prng); } } lfs2_unmount(&lfs2) => 0; // mount the new version struct lfs2p_config cfgp; memcpy(&cfgp, cfg, sizeof(cfgp)); lfs2p_t lfs2p; lfs2p_mount(&lfs2p, &cfgp) => 0; // write half COUNT files prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { // skip half but keep our prng reproducible for (lfs2_size_t j = 0; j < SIZE/2; j++) { TEST_PRNG(&prng); } // write the other half lfs2p_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_WRONLY) => 0; lfs2p_file_seek(&lfs2p, &file, SIZE/2, LFS2P_SEEK_SET) => SIZE/2; for (lfs2_size_t j = SIZE/2; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; for (lfs2_size_t k = 0; k < CHUNK; k++) { chunk[k] = TEST_PRNG(&prng) & 0xff; } lfs2p_file_write(&lfs2p, &file, chunk, CHUNK) => CHUNK; } lfs2p_file_close(&lfs2p, &file) => 0; } // can we list the directories? lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, "/") => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); char name[8]; sprintf(name, "dir%03d", i); assert(strcmp(info.name, name) == 0); } lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; // can we list the files? for (lfs2_size_t i = 0; i < COUNT; i++) { char name[8]; sprintf(name, "dir%03d", i); lfs2p_dir_t dir; lfs2p_dir_open(&lfs2p, &dir, name) => 0; struct lfs2p_info info; lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, ".") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_DIR); assert(strcmp(info.name, "..") == 0); lfs2p_dir_read(&lfs2p, &dir, &info) => 1; assert(info.type == LFS2P_TYPE_REG); sprintf(name, "file%03d", i); assert(strcmp(info.name, name) == 0); assert(info.size == SIZE); lfs2p_dir_read(&lfs2p, &dir, &info) => 0; lfs2p_dir_close(&lfs2p, &dir) => 0; } // now can we read the files? prng = 42; for (lfs2_size_t i = 0; i < COUNT; i++) { lfs2p_file_t file; char name[16]; sprintf(name, "dir%03d/file%03d", i, i); lfs2p_file_open(&lfs2p, &file, name, LFS2P_O_RDONLY) => 0; for (lfs2_size_t j = 0; j < SIZE; j += CHUNK) { uint8_t chunk[CHUNK]; lfs2p_file_read(&lfs2p, &file, chunk, CHUNK) => CHUNK; for (lfs2_size_t k = 0; k < CHUNK; k++) { assert(chunk[k] == TEST_PRNG(&prng) & 0xff); } } lfs2p_file_close(&lfs2p, &file) => 0; } lfs2p_unmount(&lfs2p) => 0; ''' ## incompatiblity tests ## # test that we fail to mount after a major version bump [cases.test_compat_major_incompat] in = 'lfs2.c' code = ''' // create a superblock lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // bump the major version // // note we're messing around with internals to do this! this // is not a user API lfs2_mount(&lfs2, cfg) => 0; lfs2_mdir_t mdir; lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; lfs2_superblock_t superblock = { .version = LFS2_DISK_VERSION + 0x00010000, .block_size = lfs2.cfg->block_size, .block_count = lfs2.cfg->block_count, .name_max = lfs2.name_max, .file_max = lfs2.file_max, .attr_max = lfs2.attr_max, }; lfs2_superblock_tole32(&superblock); lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), &superblock})) => 0; lfs2_unmount(&lfs2) => 0; // mount should now fail lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL; ''' # test that we fail to mount after a minor version bump [cases.test_compat_minor_incompat] in = 'lfs2.c' code = ''' // create a superblock lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; // bump the minor version // // note we're messing around with internals to do this! this // is not a user API lfs2_mount(&lfs2, cfg) => 0; lfs2_mdir_t mdir; lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; lfs2_superblock_t superblock = { .version = LFS2_DISK_VERSION + 0x00000001, .block_size = lfs2.cfg->block_size, .block_count = lfs2.cfg->block_count, .name_max = lfs2.name_max, .file_max = lfs2.file_max, .attr_max = lfs2.attr_max, }; lfs2_superblock_tole32(&superblock); lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), &superblock})) => 0; lfs2_unmount(&lfs2) => 0; // mount should now fail lfs2_mount(&lfs2, cfg) => LFS2_ERR_INVAL; ''' # test that we correctly bump the minor version [cases.test_compat_minor_bump] in = 'lfs2.c' if = ''' LFS2_DISK_VERSION_MINOR > 0 && DISK_VERSION == 0 ''' code = ''' // create a superblock lfs2_t lfs2; lfs2_format(&lfs2, cfg) => 0; lfs2_mount(&lfs2, cfg) => 0; lfs2_file_t file; lfs2_file_open(&lfs2, &file, "test", LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; lfs2_file_write(&lfs2, &file, "testtest", 8) => 8; lfs2_file_close(&lfs2, &file) => 0; lfs2_unmount(&lfs2) => 0; // write an old minor version // // note we're messing around with internals to do this! this // is not a user API lfs2_mount(&lfs2, cfg) => 0; lfs2_mdir_t mdir; lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; lfs2_superblock_t superblock = { .version = LFS2_DISK_VERSION - 0x00000001, .block_size = lfs2.cfg->block_size, .block_count = lfs2.cfg->block_count, .name_max = lfs2.name_max, .file_max = lfs2.file_max, .attr_max = lfs2.attr_max, }; lfs2_superblock_tole32(&superblock); lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), &superblock})) => 0; lfs2_unmount(&lfs2) => 0; // mount should still work lfs2_mount(&lfs2, cfg) => 0; struct lfs2_fsinfo fsinfo; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION-1); lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0; uint8_t buffer[8]; lfs2_file_read(&lfs2, &file, buffer, 8) => 8; assert(memcmp(buffer, "testtest", 8) == 0); lfs2_file_close(&lfs2, &file) => 0; // minor version should be unchanged lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION-1); lfs2_unmount(&lfs2) => 0; // if we write, we need to bump the minor version lfs2_mount(&lfs2, cfg) => 0; lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION-1); lfs2_file_open(&lfs2, &file, "test", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; lfs2_file_write(&lfs2, &file, "teeeeest", 8) => 8; lfs2_file_close(&lfs2, &file) => 0; // minor version should be changed lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION); lfs2_unmount(&lfs2) => 0; // and of course mount should still work lfs2_mount(&lfs2, cfg) => 0; // minor version should have changed lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION); lfs2_file_open(&lfs2, &file, "test", LFS2_O_RDONLY) => 0; lfs2_file_read(&lfs2, &file, buffer, 8) => 8; assert(memcmp(buffer, "teeeeest", 8) == 0); lfs2_file_close(&lfs2, &file) => 0; // yep, still changed lfs2_fs_stat(&lfs2, &fsinfo) => 0; assert(fsinfo.disk_version == LFS2_DISK_VERSION); lfs2_unmount(&lfs2) => 0; '''