|
|
@@ -0,0 +1,1278 @@
|
|
|
+# 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_VERSION LFS_VERSION
|
|
|
+#define LFSP_VERSION_MAJOR LFS_VERSION_MAJOR
|
|
|
+#define LFSP_VERSION_MINOR LFS_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_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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+ lfs_unmount(&lfs) => 0;
|
|
|
+'''
|
|
|
+
|
|
|
+# test we can read dirs in a new version
|
|
|
+[cases.test_compat_forward_read_dirs]
|
|
|
+defines.COUNT = 5
|
|
|
+if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION_MAJOR == LFSP_VERSION_MAJOR'
|
|
|
+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;
|
|
|
+
|
|
|
+ // 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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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_VERSION == LFSP_VERSION'
|
|
|
+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;
|
|
|
+'''
|