Files
littlefs/tests/test_ftree.toml
T
Christopher Haster c3533ab816 Some progress, with deferred attributes taking shape
Ran into an interesting macro-related bug. Turns out the way we are
doing implicit prefixing in TAG/ATTR macros sort of breaks how C macros
work a bit. The following does not compile:

  lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
          LFSR_ATTR(file->m.mdir.mid, DEFER, 0, DEFER(
              (lfsr_rbyd_t*)&file->inlined,
              LFSR_ATTR(file->buffer_pos,
                  DEFERRED(INLINED), +file->buffer_size, BUF(
                      file->buffer, file->buffer_size))))));

Or to distill it down, this does not compile:

  #define LFSR_ATTR(_data)  (LFSR_##_data)
  #define LFSR_DEFER(_data) (LFSR_##_data)
  #define LFSR_DATA(_data)  (_data)

  int a = LFSR_ATTR(DEFER(ATTR(DATA(1))));

But this does:

  #define LFSR_ATTR(_data)  (_data)
  #define LFSR_DEFER(_data) (_data)
  #define LFSR_DATA(_data)  (_data)

  int a = LFSR_ATTR(LFSR_DEFER(LFSR_ATTR(LFSR_DATA(1))));

Why? Well it turns out the whole way nested C macro's work is a big
hack.

A very reasonable design decision in C is to disallow recursive macro
expansions. Unlike C++, we don't want our preprocessor to suddenly stack
overflow. This rule is enforced by stopping macro expansion when a macro
contains itself. For example:

  #define A() B()
  #define B() A()

  A()

Expands to:

  A()
      -> B()
      -> A() (stops, probably erroring with 'A' undeclared)

But it _is_ common to want to recursively expand macro arguments. Macros
are a part of C's syntax after all, and users usually expect
expressions, such as arguments, to be context-free:

  #define A(x) (x) + 1

  A(A(A(A(A(0)))))

Naively this would expand to:

  A(A(A(A(A(0)))))
      -> (A(A(A(A(0))))) + 1 (stops)

The big hack that makes this work in C's preprocessor is the "Argument
prescan". Instead of expanding the "called" macro first, we expand any macro
inside our argument list, _then_ expand the "called" macro, and _then_
expand any new macros produced as a result of the expansion again just
for good measure.

So the above actually expands to:

  A(A(A(A(A(0)))))
      -> A(A(A(A((0) + 1))))
      -> A(A(A(((0) + 1) + 1)))
      -> A(A((((0) + 1) + 1) + 1))
      -> A(((((0) + 1) + 1) + 1) + 1)
      -> (((((0) + 1) + 1) + 1) + 1) + 1

This is still recursive actually! But the recursion is limited to the
actual length of the source code, so the developers likely thought this
was a reasonable tradeoff.

But what does this mean for our implicit prefixing?

  #define P_A(x) P_##x
  #define P_B(x) P_##x
  #define P_C(x) (x)

  P_A(B(A(C(0))))

None of A, B, C are in scope without prefixes, so they get expanded
after the "called" macro's expansion:

  P_A(B(A(C)))
      -> P_B(A(C(0)))
      -> P_A(C(0)) (stops)

But this breaks when we hit the nested P_A macro.

---

For now I've gone with the temporary, and extra hacky, solution of
introducing a second LFSR_ATTR_ macro. This nesting of ATTR macros only
happens because of shrubs, and only ever goes 2 layers deep.

In the future maybe we should move away from implicit prefixing. They
have a few rough corners and may be a bit confusing for anyone new to
the code.
2023-10-13 22:00:55 -05:00

1016 lines
32 KiB
TOML

# Test basic file operations
after = ['test_dtree']
# test creation/deletion
[cases.test_ftree_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 buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => 0;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test we can write some data, should be inlined
[cases.test_ftree_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];
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_ftree_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];
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_ftree_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 buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# a file is not a directory
[cases.test_ftree_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 buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# a directory is not a file
[cases.test_ftree_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];
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 a larger file? this should need to write an inlined tree
[cases.test_ftree_sprout]
defines.SIZE = '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[8192];
uint32_t prng = 42;
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = TEST_PRNG(&prng);
}
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[8192];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, 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;
#'''