# Test basic file operations after = ['test_dtree'] # test creation/deletion [cases.test_files_create] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == 0); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => 0; // try reading uint8_t rbuf[8192]; lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => 0; lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # test we can write some data, should be inlined [cases.test_files_hello] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; uint8_t wbuf[8192]; strcpy((char*)wbuf, "Hello World!"); lfs_size_t wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => wsize; // try reading uint8_t rbuf[8192]; memset(rbuf, 0xaa, sizeof(rbuf)); lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize; assert(memcmp(rbuf, wbuf, wsize) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # test we can rewrite a file [cases.test_files_trunc] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; uint8_t wbuf[8192]; strcpy((char*)wbuf, "Oh no!"); lfs_size_t wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // rewrite the file lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; strcpy((char*)wbuf, "Hello World!"); wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => wsize; // try reading uint8_t rbuf[8192]; memset(rbuf, 0xaa, sizeof(rbuf)); lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize; assert(memcmp(rbuf, wbuf, wsize) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # check for LFS_F_EXCL errors [cases.test_files_excl] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; uint8_t wbuf[8192]; strcpy((char*)wbuf, "Hello World!"); lfs_size_t wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // try to recreate file, this should error lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => wsize; // try reading uint8_t rbuf[8192]; memset(rbuf, 0xaa, sizeof(rbuf)); lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize; assert(memcmp(rbuf, wbuf, wsize) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # a file is not a directory [cases.test_files_file_not_dir] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; uint8_t wbuf[8192]; strcpy((char*)wbuf, "Hello World!"); lfs_size_t wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // try to open our file as a directory lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "hello") => LFS_ERR_NOTDIR; // try to create a directory on top of our file lfsr_mkdir(&lfs, "hello") => LFS_ERR_EXIST; // try to rename a directory onto our file lfsr_mkdir(&lfs, "not_hello") => 0; lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); // and with dir read lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "not_hello") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => wsize; // try reading uint8_t rbuf[8192]; memset(rbuf, 0xaa, sizeof(rbuf)); lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize; assert(memcmp(rbuf, wbuf, wsize) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # a directory is not a file [cases.test_files_dir_not_file] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a directory lfsr_mkdir(&lfs, "hello") => 0; // try reading our directory as a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => LFS_ERR_ISDIR; // try writing our directory as a file lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_ISDIR; // try rename a file on top of our directory lfsr_file_open(&lfs, &file, "not_hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; uint8_t wbuf[8192]; strcpy((char*)wbuf, "Hello World!"); lfs_size_t wsize = strlen((const char*)wbuf); lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our dir with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_DIR); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "not_hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == wsize); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // did we corrupt our renaming file? // try reading our file lfsr_file_open(&lfs, &file, "not_hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => wsize; // try reading uint8_t rbuf[8192]; memset(rbuf, 0xaa, sizeof(rbuf)); lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize; assert(memcmp(rbuf, wbuf, wsize) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # try writing larger files? # # at 2*CACHE_SIZE we need an inlined tree # ? single block? # at 2*BLOCK_SIZE we need a b-tree # [cases.test_files_more] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; uint8_t wbuf[SIZE]; uint32_t prng = 42; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => SIZE; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # more complex writing patterns to inlined files # write files incrementally [cases.test_files_incr] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] defines.SYNC = [false, true] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; uint8_t wbuf[SIZE]; uint32_t prng = 42; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { lfsr_file_write(&lfs, &file, &wbuf[i], CHUNK) => CHUNK; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // note the switch to append here lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_APPEND) => 0; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => SIZE; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # overwrite files [cases.test_files_overwrite] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] # bit 0 => first chunk # bit 1 => middle chunk # bit 2 => last chunk defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] # 0 => in-order # 1 => reversed defines.ORDER = [0, 1] defines.SYNC = [false, true] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; uint32_t prng = 42; for (lfs_size_t i = 0; i < SIZE; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write first chunk? if (MASK & 1) { if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write second chunk? if (MASK & 2) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) => SIZE/2 - CHUNK/2; lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write third chunk? if (MASK & 4) { if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => SIZE; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; // does our file match our simulation? assert(memcmp(rbuf, sim, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # similar to overwrite files, but without underlying data [cases.test_files_holes] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] # bit 0 => first chunk # bit 1 => middle chunk # bit 2 => last chunk defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] # 0 => in-order # 1 => reversed defines.ORDER = [0] # TODO 1? defines.SYNC = [false, true] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; uint32_t prng = 42; memset(sim, 0, SIZE); // we may not write the entire file lfs_off_t size = (MASK & 0x4) ? SIZE : (MASK & 0x2) ? SIZE/2 + (CHUNK+2-1)/2 : (MASK & 0x1) ? CHUNK : 0; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write first chunk? if (MASK & 1) { if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write second chunk? if (MASK & 2) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) => SIZE/2 - CHUNK/2; lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write third chunk? if (MASK & 4) { if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => size; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; // does our file match our simulation? assert(memcmp(rbuf, sim, size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # writing any data structure backwards always reveals issues [cases.test_files_reversed_overwrite] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] defines.SYNC = [false, true] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; uint32_t prng = 42; for (lfs_size_t i = 0; i < SIZE; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write to file incrementally and backwards for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t j = 0; j < CHUNK; j++) { sim[SIZE-i-CHUNK+j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-i-CHUNK, LFS_SEEK_SET) => SIZE-i-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-i-CHUNK], CHUNK) => CHUNK; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => SIZE; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; // does our file match our simulation? assert(memcmp(rbuf, sim, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # writing any data structure backwards always reveals issues [cases.test_files_reversed_holes] defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] defines.SYNC = [false, true] defines.REMOUNT = [false, true] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, CFG); if (err) { lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; } // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; uint32_t prng = 42; memset(sim, 0, SIZE); // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } // write to file incrementally and backwards for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t j = 0; j < CHUNK; j++) { sim[SIZE-i-CHUNK+j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-i-CHUNK, LFS_SEEK_SET) => SIZE-i-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-i-CHUNK], CHUNK) => CHUNK; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => SIZE; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; // does our file match our simulation? assert(memcmp(rbuf, sim, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # fuzz testing [cases.test_files_fuzz_aligned] defines.N = 100 defines.SEED = 'range(100)' defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] defines.INIT = [false, true] defines.SYNC = [false, true] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; lfs_off_t size; uint32_t prng = SEED; if (INIT) { for (lfs_size_t i = 0; i < SIZE; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; size = SIZE; } else { memset(sim, 0, SIZE); size = 0; } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } for (lfs_size_t i = 0; i < N; i++) { // choose a random chunk-aligned location lfs_off_t off = (TEST_PRNG(&prng) % (SIZE/CHUNK)) * CHUNK; // update sim for (lfs_size_t j = 0; j < CHUNK; j++) { sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); } size = lfs_max32(size, off+CHUNK); // update file lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &file, &sim[off], CHUNK) => CHUNK; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => size; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; // does our file match our simulation? assert(memcmp(rbuf, sim, size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # fuzz testing [cases.test_files_fuzz_unaligned] defines.N = 100 defines.SEED = 'range(100)' defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] # chunk is more an upper limit here defines.CHUNK = ['CACHE_SIZE/2', '4'] defines.INIT = [false, true] defines.SYNC = [false, true] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a file, truncating in case of powerloss lfsr_file_t file; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; // simulate our file in ram uint8_t sim[SIZE]; lfs_off_t size; uint32_t prng = SEED; if (INIT) { for (lfs_size_t i = 0; i < SIZE; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; size = SIZE; } else { memset(sim, 0, SIZE); size = 0; } // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } for (lfs_size_t i = 0; i < N; i++) { // choose a random location lfs_off_t off = TEST_PRNG(&prng) % SIZE; // and a random size, up to the chunk size lfs_size_t chunk = lfs_min32( TEST_PRNG(&prng) % CHUNK, SIZE - off); // update sim for (lfs_size_t j = 0; j < chunk; j++) { sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); } if (chunk != 0) { size = lfs_max32(size, off+chunk); } // update file lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; // sync? if (SYNC) { lfsr_file_sync(&lfs, &file) => 0; } // remount? if (REMOUNT) { lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; } } lfsr_file_close(&lfs, &file) => 0; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check our file with stat struct lfs_info info; lfsr_stat(&lfs, "hello", &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); // and with dir read lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == size); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // try reading our file lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; // is size correct? lfsr_file_size(&lfs, &file) => size; // try reading uint8_t rbuf[2*SIZE]; memset(rbuf, 0xaa, 2*SIZE); lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; // does our file match our simulation? assert(memcmp(rbuf, sim, size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # #[cases.test_files_simple] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "hello", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # lfs_size_t size = strlen("Hello World!")+1; # uint8_t buffer[1024]; # strcpy((char*)buffer, "Hello World!"); # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, buffer, size) => size; # assert(strcmp((char*)buffer, "Hello World!") == 0); # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_large] #defines.SIZE = [32, 8192, 262144, 0, 7, 8193] #defines.CHUNKSIZE = [31, 16, 33, 1, 1023] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # // write # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "avacado", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # uint32_t prng = 1; # uint8_t buffer[1024]; # for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE; # prng = 1; # for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_rewrite] #defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] #defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] #defines.CHUNKSIZE = [31, 16, 1] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # // write # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # uint8_t buffer[1024]; # lfs_file_open(&lfs, &file, "avacado", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # uint32_t prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE1; # prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // rewrite # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0; # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2); # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # if (SIZE1 > SIZE2) { # prng = 1; # for (lfs_size_t b = 0; b < SIZE2; b++) { # TEST_PRNG(&prng); # } # for (lfs_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_append] #defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] #defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] #defines.CHUNKSIZE = [31, 16, 1] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # // write # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # uint8_t buffer[1024]; # lfs_file_open(&lfs, &file, "avacado", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # uint32_t prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE1; # prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // append # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0; # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE1 + SIZE2; # prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_truncate] #defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] #defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] #defines.CHUNKSIZE = [31, 16, 1] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # // write # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # uint8_t buffer[1024]; # lfs_file_open(&lfs, &file, "avacado", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # uint32_t prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE1; # prng = 1; # for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // truncate # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0; # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // read # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE2; # prng = 2; # for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_reentrant_write] #defines.SIZE = [32, 0, 7, 2049] #defines.CHUNKSIZE = [31, 16, 65] #reentrant = true #code = ''' # lfs_t lfs; # int err = lfs_mount(&lfs, cfg); # if (err) { # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # } # # lfs_file_t file; # uint8_t buffer[1024]; # err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); # assert(err == LFS_ERR_NOENT || err == 0); # if (err == 0) { # // can only be 0 (new file) or full size # lfs_size_t size = lfs_file_size(&lfs, &file); # assert(size == 0 || size == SIZE); # lfs_file_close(&lfs, &file) => 0; # } # # // write # lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0; # uint32_t prng = 1; # for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # } # lfs_file_close(&lfs, &file) => 0; # # // read # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE; # prng = 1; # for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_reentrant_write_sync] #defines = [ # # append (O(n)) # {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, # # truncate (O(n^2)) # {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, # # rewrite (O(n^2)) # {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, #] #reentrant = true #code = ''' # lfs_t lfs; # int err = lfs_mount(&lfs, cfg); # if (err) { # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # } # # lfs_file_t file; # uint8_t buffer[1024]; # err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); # assert(err == LFS_ERR_NOENT || err == 0); # if (err == 0) { # // with syncs we could be any size, but it at least must be valid data # lfs_size_t size = lfs_file_size(&lfs, &file); # assert(size <= SIZE); # uint32_t prng = 1; # for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, size-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_close(&lfs, &file) => 0; # } # # // write # lfs_file_open(&lfs, &file, "avacado", # LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0; # lfs_size_t size = lfs_file_size(&lfs, &file); # assert(size <= SIZE); # uint32_t prng = 1; # lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0; # for (lfs_size_t b = 0; b < skip; b++) { # TEST_PRNG(&prng); # } # for (lfs_size_t i = skip; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # for (lfs_size_t b = 0; b < chunk; b++) { # buffer[b] = TEST_PRNG(&prng) & 0xff; # } # lfs_file_write(&lfs, &file, buffer, chunk) => chunk; # lfs_file_sync(&lfs, &file) => 0; # } # lfs_file_close(&lfs, &file) => 0; # # // read # lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; # lfs_file_size(&lfs, &file) => SIZE; # prng = 1; # for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { # lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); # lfs_file_read(&lfs, &file, buffer, chunk) => chunk; # for (lfs_size_t b = 0; b < chunk; b++) { # assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); # } # } # lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_many] #defines.N = 300 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # // create N files of 7 bytes # lfs_mount(&lfs, cfg) => 0; # for (int i = 0; i < N; i++) { # lfs_file_t file; # char path[1024]; # sprintf(path, "file_%03d", i); # lfs_file_open(&lfs, &file, path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # char wbuffer[1024]; # lfs_size_t size = 7; # sprintf(wbuffer, "Hi %03d", i); # lfs_file_write(&lfs, &file, wbuffer, size) => size; # lfs_file_close(&lfs, &file) => 0; # # char rbuffer[1024]; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(strcmp(rbuffer, wbuffer) == 0); # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_many_power_cycle] #defines.N = 300 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # // create N files of 7 bytes # lfs_mount(&lfs, cfg) => 0; # for (int i = 0; i < N; i++) { # lfs_file_t file; # char path[1024]; # sprintf(path, "file_%03d", i); # lfs_file_open(&lfs, &file, path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; # char wbuffer[1024]; # lfs_size_t size = 7; # sprintf(wbuffer, "Hi %03d", i); # lfs_file_write(&lfs, &file, wbuffer, size) => size; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # char rbuffer[1024]; # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(strcmp(rbuffer, wbuffer) == 0); # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; #''' # #[cases.test_files_many_power_loss] #defines.N = 300 #reentrant = true #code = ''' # lfs_t lfs; # int err = lfs_mount(&lfs, cfg); # if (err) { # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # } # // create N files of 7 bytes # for (int i = 0; i < N; i++) { # lfs_file_t file; # char path[1024]; # sprintf(path, "file_%03d", i); # err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT); # char wbuffer[1024]; # lfs_size_t size = 7; # sprintf(wbuffer, "Hi %03d", i); # if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) { # lfs_file_write(&lfs, &file, wbuffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # char rbuffer[1024]; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(strcmp(rbuffer, wbuffer) == 0); # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; #'''