# Test the annoying subtle corner cases of directory seeking+reading # # Note there may be some overlap with test_dtree, since some dir operations # are needed to validate the directory tree works after = 'test_dtree' # test some dir functions [cases.test_dseek_tell] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; } // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } // read our directory // // Note tell's value is not guaranteed! We can test the exact value only // because these tests are tightly bound to the current littlefs version. // lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; lfsr_dir_tell(&lfs, &dir) => 0; struct lfs_info info; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_tell(&lfs, &dir) => 1; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (lfs_size_t i = 0; i < N; i++) { lfsr_dir_tell(&lfs, &dir) => 2 + i; char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_rewind] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; } // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; // read our directory once lfsr_dir_tell(&lfs, &dir) => 0;; struct lfs_info info; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_tell(&lfs, &dir) => 1; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (lfs_size_t i = 0; i < N; i++) { lfsr_dir_tell(&lfs, &dir) => 2 + i; char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_tell(&lfs, &dir) => 2 + N; // now read it again lfsr_dir_rewind(&lfs, &dir) => 0; lfsr_dir_tell(&lfs, &dir) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_tell(&lfs, &dir) => 1; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (lfs_size_t i = 0; i < N; i++) { lfsr_dir_tell(&lfs, &dir) => 2 + i; char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_seek] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; } // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; // read our directory once lfsr_dir_tell(&lfs, &dir) => 0; struct lfs_info info; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_tell(&lfs, &dir) => 1; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (lfs_size_t i = 0; i < N; i++) { lfsr_dir_tell(&lfs, &dir) => 2 + i; char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_tell(&lfs, &dir) => 2 + N; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_tell(&lfs, &dir) => 2 + N; // now try to seek to each entry explicitly lfsr_dir_seek(&lfs, &dir, 0) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_seek(&lfs, &dir, 1) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (lfs_size_t i = 0; i < N; i++) { lfsr_dir_seek(&lfs, &dir, 2 + i) => 0; char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_seek(&lfs, &dir, 2 + N) => 0; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_seek(&lfs, &dir, 2 + N) => 0; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' # test we don't ever get extra entries back after we reach # the end of a directory [cases.test_dseek_read_idempotent] defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # neighbors only make sense if we have a parent if = 'PARENT || NEIGHBORS == 0' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } char name[256]; sprintf(name, "%s/ardvark", (PARENT ? "pricklypear" : "")); lfsr_mkdir(&lfs, name) => 0; // read to the end lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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, "ardvark") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_tell(&lfs, &dir) => 3; // reading again should still return noent lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; // seeking past the end of the directory should still return noent lfsr_dir_seek(&lfs, &dir, 3) => 0; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_seek(&lfs, &dir, 4) => 0; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_seek(&lfs, &dir, 1000) => 0; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; // but we should be able to read again lfsr_dir_rewind(&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, "ardvark") == 0); assert(info.type == LFS_TYPE_DIR); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' # test dir read works when under filesystem modifications # # this is a bit complex and subtle [cases.test_dseek_read_with_mkdirs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' # where do we mkdir? defines.J = 'range(6)' defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # neighbors only make sense if we have a parent if = 'PARENT || NEIGHBORS == 0' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i+1); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i+1); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // make a dir at J char name[256]; sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)J); lfsr_mkdir(&lfs, name) => 0; // seek after mkdir? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading, though we may pick up J for (lfs_size_t i = I + (I >= J ? 1 : 0); i < N+1; i++) { char name[256]; sprintf(name, "dir%04d%s", i+1 - (i >= J ? 1 : 0), (i == J ? "_" : "")); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_read_with_rms] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' # where do we rm? defines.J = 'range(5)' defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # neighbors only make sense if we have a parent if = 'PARENT || NEIGHBORS == 0' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // remove the dir at J char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J); lfsr_remove(&lfs, name) => 0; // seek after remove? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading for (lfs_size_t i = I; i < N; i++) { if (i == J) { continue; } char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_read_with_mvs] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' # where do we rm? defines.J = 'range(5)' defines.K = 'range(5)' defines.BEFORE = [false, true] # 0 => no # 1 => yes # 2 => yes, and rename to new parent defines.PARENT = [0, 1, 2] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # neighbors only make sense if we have a parent if = 'PARENT || NEIGHBORS == 0' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (PARENT >= 2) { lfsr_mkdir(&lfs, "quiabentia") => 0; } if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // rename the dir at J char old_name[256]; sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J); char new_name[256]; sprintf(new_name, "%s/%smved%04d", (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : ""), (BEFORE ? "0" : ""), (int)J); lfsr_rename(&lfs, old_name, new_name) => 0; // seek after remove? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading for (lfs_size_t i = I; i < N; i++) { if (i == J) { continue; } char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } int err = lfsr_dir_read(&lfs, &dir, &info); assert(err == LFS_ERR_NOENT || (!BEFORE && PARENT < 2)); lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' # dir reads with 2x ops have better chances of catching bugs that depend on # invalid dir states [cases.test_dseek_read_with_2_mkdirs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' # where do we mkdir? defines.J = 'range(6)' defines.K = 'range(6)' defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] if = [ 'J != K', # neighbors only make sense if we have a parent 'PARENT || NEIGHBORS == 0', ] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i+1); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i+1); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // make a dir at J char name[256]; sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)J); lfsr_mkdir(&lfs, name) => 0; // make a dir at K sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)K); lfsr_mkdir(&lfs, name) => 0; // seek after mkdir? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading, though we may pick up J for (lfs_size_t i = I + (I >= J ? 1 : 0) + (I >= K ? 1 : 0); i < N+2; i++) { char name[256]; sprintf(name, "dir%04d%s", i+1 - (i >= J + (J >= K ? 1 : 0) ? 1 : 0) - (i >= K + (K >= J ? 1 : 0) ? 1 : 0), (i == J + (J > K ? 1 : 0) || i == K + (K > J ? 1 : 0) ? "_" : "")); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_read_with_2_rms] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' # where do we rm? defines.J = 'range(5)' defines.K = 'range(5)' defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] if = [ 'J != K', # neighbors only make sense if we have a parent 'PARENT || NEIGHBORS == 0', ] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // remove the dir at J char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J); lfsr_remove(&lfs, name) => 0; // remove the dir at K sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)K); lfsr_remove(&lfs, name) => 0; // seek after remove? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading for (lfs_size_t i = I; i < N; i++) { if (i == J || i == K) { continue; } char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_dseek_read_with_2_mvs] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' # where do we rm? defines.J = 'range(5)' defines.K = 'range(5)' defines.BEFORE = [false, true] # 0 => no # 1 => yes # 2 => yes, and rename to new parent defines.PARENT = [0, 1, 2] # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # neighbors only make sense if we have a parent if = [ 'J != K', # neighbors only make sense if we have a parent 'PARENT || NEIGHBORS == 0', ] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; if (PARENT) { lfsr_mkdir(&lfs, "pricklypear") => 0; if (PARENT >= 2) { lfsr_mkdir(&lfs, "quiabentia") => 0; } if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // rename the dir at J char old_name[256]; sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J); char new_name[256]; sprintf(new_name, "%s/%smved%04d", (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : ""), (BEFORE ? "0" : ""), (int)J); lfsr_rename(&lfs, old_name, new_name) => 0; // rename the dir at K sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)K); sprintf(new_name, "%s/%smved%04d", (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : ""), (BEFORE ? "0" : ""), (int)K); lfsr_rename(&lfs, old_name, new_name) => 0; // seek after remove? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // we should be able to keep reading for (lfs_size_t i = I; i < N; i++) { if (i == J || i == K) { continue; } char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } int err = lfsr_dir_read(&lfs, &dir, &info); assert(err == LFS_ERR_NOENT || (!BEFORE && PARENT < 2)); lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' # test removing the directory we are iterating over [cases.test_dseek_read_rm] defines.N = 5 # where in the dir read do we remove? defines.I = 'range(6)' # bit 0x2 = left neighbor # bit 0x1 = right neighbor defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfsr_mkdir(&lfs, "pricklypear") => 0; if (NEIGHBORS & 0x2) { assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000); lfsr_mkdir(&lfs, "a_IplRNrPH") => 0; lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0; } if (NEIGHBORS & 0x1) { assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff); lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0; lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0; } // create our directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "pricklypear/dir%04d", i+1); lfsr_mkdir(&lfs, name) => 0; } // start reading lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "pricklypear") => 0; struct lfs_info info; 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); // read until I for (lfs_size_t i = 0; i < I; i++) { char name[256]; sprintf(name, "dir%04d", i+1); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } // remove the directory for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "pricklypear/dir%04d", i+1); lfsr_remove(&lfs, name) => 0; } lfsr_remove(&lfs, "pricklypear") => 0; // seek after mkdir? if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } // try to read, but this should return an error lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' ## Recursive tests # Recursive here just refers to removing entries in a directory while # iterating over the directory # # This is a useful feature, but it's unintuitive if this should have # well-defined behavior, so make sure to test for it [cases.test_dseek_recursive_rm] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # limit powerloss testing due to time if = '!TEST_PL || N <= 32' 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; } if (PARENT) { err = lfsr_mkdir(&lfs, "pricklypear"); assert(!err || (TEST_PL && err == LFS_ERR_EXIST)); } // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); err = lfsr_mkdir(&lfs, name); assert(!err || (TEST_PL && err == LFS_ERR_EXIST)); } // check that our mkdir worked for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; char name2[256]; sprintf(name2, "dir%04d", i); assert(strcmp(info.name, name2) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // now remove directories recursively lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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); for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); char path[1024]; sprintf(path, "%s/%s", (PARENT ? "pricklypear" : ""), info.name); lfsr_remove(&lfs, path) => 0; // seek between removes? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // check that our removes worked for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT; } lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; ''' # Recursive here just refers to renaming entries in a directory while # iterating over the directory # # This is a useful feature, but it's unintuitive if this should have # well-defined behavior, so make sure to test for it [cases.test_dseek_recursive_mv] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.BEFORE = [false, true] # 0 => no # 1 => yes # 2 => yes, and rename to new parent defines.PARENT = [0, 1, 2] # 0 => don't seek # 1 => seek # 2 => rewind then seek defines.SEEK = [0, 1, 2] # limit powerloss testing due to time if = '!TEST_PL || N <= 32' 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; } if (PARENT) { err = lfsr_mkdir(&lfs, "pricklypear"); assert(!err || (TEST_PL && err == LFS_ERR_EXIST)); if (PARENT >= 2) { err = lfsr_mkdir(&lfs, "quiabentia"); assert(!err || (TEST_PL && err == LFS_ERR_EXIST)); } } // check if we have already started renaming, in case of powerloss struct lfs_info info; err = lfsr_stat(&lfs, (PARENT == 1 ? (BEFORE ? "pricklypear/0mved0000" : "pricklypear/mved0000") : PARENT >= 2 ? (BEFORE ? "quiabentia/0mved0000" : "quiabentia/mved0000") : (BEFORE ? "/0mved0000" : "/mved0000")), &info); if (err == LFS_ERR_NOENT) { // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); err = lfsr_mkdir(&lfs, name); assert(!err || (TEST_PL && err == LFS_ERR_EXIST)); } // check that our mkdir worked for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; char name2[256]; sprintf(name2, "dir%04d", i); assert(strcmp(info.name, name2) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0; struct lfs_info info; 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); for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%04d", i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; } // now rename directories recursively lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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); for (lfs_size_t i = 0;; i++) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); // reached the end? if (err == LFS_ERR_NOENT) { break; } // skip already moved? if (memcmp(info.name, "dir", strlen("dir")) != 0) { continue; } assert(i < 2*N); assert(info.type == LFS_TYPE_DIR); char old_path[1024]; sprintf(old_path, "%s/%s", (PARENT ? "pricklypear" : ""), info.name); char new_path[1024]; sprintf(new_path, "%s/%smved%s", (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : ""), (BEFORE ? "0" : ""), &info.name[strlen("dir")]); err = lfsr_rename(&lfs, old_path, new_path); assert(!err || (TEST_PL && err == LFS_ERR_NOENT)); // seek between renames? this tests that the internal position is // updated correctly if (SEEK) { lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir); assert(off >= 0); if (SEEK >= 2) { lfsr_dir_rewind(&lfs, &dir) => 0; } lfsr_dir_seek(&lfs, &dir, off) => 0; } } lfsr_dir_close(&lfs, &dir) => 0; // check that our renames worked for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT; sprintf(name, "%s/%smved%04d", (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : ""), (BEFORE ? "0" : ""), i); lfsr_stat(&lfs, name, &info) => 0; char name2[256]; sprintf(name2, "%smved%04d", (BEFORE ? "0" : ""), i); assert(strcmp(info.name, name2) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_open(&lfs, &dir, (PARENT == 1 ? "pricklypear" : PARENT >= 2 ? "quiabentia" : "/")) => 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); for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; lfsr_unmount(&lfs) => 0; '''