From 92844cce3eed520c4a4219da585d194d55928e14 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 18 Jun 2025 23:33:24 -0500 Subject: [PATCH] kv: Added *_set_zero and *_set_null tests These are high-risk corner cases for the key-value API, so we should test them. At one point I was relying on an optional buffer parameter in lfs3_file_sync_, but that would have broken if lfs3_set's buffer was NULL. --- tests/test_attrs.toml | 465 ++++++++++++++++++++++++++++++++++++++++++ tests/test_kv.toml | 172 ++++++++++++++++ 2 files changed, 637 insertions(+) diff --git a/tests/test_attrs.toml b/tests/test_attrs.toml index d40bd9da..24e289fc 100644 --- a/tests/test_attrs.toml +++ b/tests/test_attrs.toml @@ -464,6 +464,249 @@ code = ''' lfs3_unmount(&lfs3) => 0; ''' +# test setting attrs to zero size +# +# this is _not_ the same as removing +# +[cases.test_attrs_setattr_zero] +# type of file to attach attrs to +# FILETYPE=0 => regular file +# FILETYPE=1 => stickynote +# FILETYPE=2 => directory +# FILETYPE=3 => root +defines.FILETYPE = [0, 1, 2, 3] +# zero based on this mask +defines.MASK = 'range(0x8)' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + const char *path; + lfs3_file_t file; + // create a file? + if (FILETYPE == 0) { + path = "cat"; + lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow"); + lfs3_file_close(&lfs3, &file) => 0; + + // create a stickynote? + } else if (FILETYPE == 1) { + path = "snail"; + lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise")) + => strlen("snailnoise"); + + // create a dir? + } else if (FILETYPE == 2) { + path = "armadillo"; + lfs3_mkdir(&lfs3, path) => 0; + + // do nothing for root + } else { + path = "/"; + } + + // create some attrs + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0; + + // remove some attrs + if (MASK & 0x1) { + lfs3_setattr(&lfs3, path, 'a', (const uint8_t*)1, 0) => 0; + } + if (MASK & 0x2) { + lfs3_setattr(&lfs3, path, 'b', (const uint8_t*)1, 0) => 0; + } + if (MASK & 0x4) { + lfs3_setattr(&lfs3, path, 'c', (const uint8_t*)1, 0) => 0; + } + + if (FILETYPE == 1) { + lfs3_file_close(&lfs3, &file) => 0; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the attr sizes + if (MASK & 0x1) { + lfs3_sizeattr(&lfs3, path, 'a') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'a') => strlen(a); + } + if (MASK & 0x2) { + lfs3_sizeattr(&lfs3, path, 'b') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'b') => strlen(b); + } + if (MASK & 0x4) { + lfs3_sizeattr(&lfs3, path, 'c') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'c') => strlen(c); + } + + // try reading the full attrs + uint8_t rbuf[256]; + if (MASK & 0x1) { + lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) + => strlen(a); + assert(memcmp(rbuf, a, strlen(a)) == 0); + } + if (MASK & 0x2) { + lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) + => strlen(b); + assert(memcmp(rbuf, b, strlen(b)) == 0); + } + if (MASK & 0x4) { + lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) + => strlen(c); + assert(memcmp(rbuf, c, strlen(c)) == 0); + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test setting attrs to zero size + null +# +# this can trip up naive internal logic +# +[cases.test_attrs_setattr_null] +# type of file to attach attrs to +# FILETYPE=0 => regular file +# FILETYPE=1 => stickynote +# FILETYPE=2 => directory +# FILETYPE=3 => root +defines.FILETYPE = [0, 1, 2, 3] +# zero based on this mask +defines.MASK = 'range(0x8)' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + const char *path; + lfs3_file_t file; + // create a file? + if (FILETYPE == 0) { + path = "cat"; + lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow"); + lfs3_file_close(&lfs3, &file) => 0; + + // create a stickynote? + } else if (FILETYPE == 1) { + path = "snail"; + lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise")) + => strlen("snailnoise"); + + // create a dir? + } else if (FILETYPE == 2) { + path = "armadillo"; + lfs3_mkdir(&lfs3, path) => 0; + + // do nothing for root + } else { + path = "/"; + } + + // create some attrs + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0; + + // remove some attrs + if (MASK & 0x1) { + lfs3_setattr(&lfs3, path, 'a', NULL, 0) => 0; + } + if (MASK & 0x2) { + lfs3_setattr(&lfs3, path, 'b', NULL, 0) => 0; + } + if (MASK & 0x4) { + lfs3_setattr(&lfs3, path, 'c', NULL, 0) => 0; + } + + if (FILETYPE == 1) { + lfs3_file_close(&lfs3, &file) => 0; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the attr sizes + if (MASK & 0x1) { + lfs3_sizeattr(&lfs3, path, 'a') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'a') => strlen(a); + } + if (MASK & 0x2) { + lfs3_sizeattr(&lfs3, path, 'b') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'b') => strlen(b); + } + if (MASK & 0x4) { + lfs3_sizeattr(&lfs3, path, 'c') => 0; + } else { + lfs3_sizeattr(&lfs3, path, 'c') => strlen(c); + } + + // try reading the full attrs + uint8_t rbuf[256]; + if (MASK & 0x1) { + lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) + => strlen(a); + assert(memcmp(rbuf, a, strlen(a)) == 0); + } + if (MASK & 0x2) { + lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) + => strlen(b); + assert(memcmp(rbuf, b, strlen(b)) == 0); + } + if (MASK & 0x4) { + lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) + => strlen(c); + assert(memcmp(rbuf, c, strlen(c)) == 0); + } + } + + lfs3_unmount(&lfs3) => 0; +''' # test the full range of attrs [cases.test_attrs_all] @@ -2325,6 +2568,228 @@ code = ''' lfs3_unmount(&lfs3) => 0; ''' +# test that we can set attrs to zero size +# +# this is _not_ the same as removing +# +[cases.test_attrs_fattr_zero] +defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR'] +defines.MUTSIZE = [false, true] +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + // create a file + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow"); + lfs3_file_close(&lfs3, &file) => 0; + + // create some attrs + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0; + + // try opening a file with attrs + uint8_t a_buf[256]; + lfs3_ssize_t a_size; + uint8_t b_buf[256]; + lfs3_ssize_t b_size; + uint8_t c_buf[256]; + lfs3_ssize_t c_size; + struct lfs3_attr attrs[] = { + { + .type = 'a', + .flags = MODE, + .buffer = a_buf, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(a_buf) + : 0, + .size = (MUTSIZE) ? &a_size : NULL, + }, + { + .type = 'b', + .flags = MODE, + .buffer = b_buf, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(b_buf) + : 0, + .size = (MUTSIZE) ? &b_size : NULL, + }, + { + .type = 'c', + .flags = MODE, + .buffer = c_buf, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(c_buf) + : 0, + .size = (MUTSIZE) ? &c_size : NULL, + } + }; + struct lfs3_file_config filecfg = { + .attrs = attrs, + .attr_count = 3, + }; + lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0; + + if (MODE == LFS3_A_RDWR && MUTSIZE) { + // did we read the attrs correctly? + assert(a_size == strlen(a)); + assert(memcmp(a_buf, a, strlen(a)) == 0); + assert(b_size == strlen(b)); + assert(memcmp(b_buf, b, strlen(b)) == 0); + assert(c_size == strlen(c)); + assert(memcmp(c_buf, c, strlen(c)) == 0); + } + + // mark the attrs as zero + a_size = 0; + b_size = 0; + c_size = 0; + + // write and close our file to write the attrs out to disk + lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao"); + lfs3_file_close(&lfs3, &file) => 0; + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the attr sizes + lfs3_sizeattr(&lfs3, "cat", 'a') => 0; + lfs3_sizeattr(&lfs3, "cat", 'b') => 0; + lfs3_sizeattr(&lfs3, "cat", 'c') => 0; + // try reading the attrs + uint8_t rbuf[256]; + lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => 0; + lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => 0; + lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => 0; + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test that we can set attrs to zero size + null +# +# this can trip up naive internal logic +# +[cases.test_attrs_fattr_null] +defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR'] +defines.MUTSIZE = [false, true] +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + // create a file + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; + lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow"); + lfs3_file_close(&lfs3, &file) => 0; + + // create some attrs + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0; + + // try opening a file with attrs + uint8_t a_buf[256]; + lfs3_ssize_t a_size; + uint8_t b_buf[256]; + lfs3_ssize_t b_size; + uint8_t c_buf[256]; + lfs3_ssize_t c_size; + struct lfs3_attr attrs[] = { + { + .type = 'a', + .flags = MODE, + .buffer = (MUTSIZE) + ? a_buf + : NULL, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(a_buf) + : 0, + .size = (MUTSIZE) ? &a_size : NULL, + }, + { + .type = 'b', + .flags = MODE, + .buffer = (MUTSIZE) + ? b_buf + : NULL, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(b_buf) + : 0, + .size = (MUTSIZE) ? &b_size : NULL, + }, + { + .type = 'c', + .flags = MODE, + .buffer = (MUTSIZE) + ? c_buf + : NULL, + .buffer_size = (MUTSIZE) + ? (lfs3_ssize_t)sizeof(c_buf) + : 0, + .size = (MUTSIZE) ? &c_size : NULL, + } + }; + struct lfs3_file_config filecfg = { + .attrs = attrs, + .attr_count = 3, + }; + lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0; + + if (MODE == LFS3_A_RDWR && MUTSIZE) { + // did we read the attrs correctly? + assert(a_size == strlen(a)); + assert(memcmp(a_buf, a, strlen(a)) == 0); + assert(b_size == strlen(b)); + assert(memcmp(b_buf, b, strlen(b)) == 0); + assert(c_size == strlen(c)); + assert(memcmp(c_buf, c, strlen(c)) == 0); + } + + // mark the attrs as zero + a_size = 0; + b_size = 0; + c_size = 0; + + // write and close our file to write the attrs out to disk + lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao"); + lfs3_file_close(&lfs3, &file) => 0; + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the attr sizes + lfs3_sizeattr(&lfs3, "cat", 'a') => 0; + lfs3_sizeattr(&lfs3, "cat", 'b') => 0; + lfs3_sizeattr(&lfs3, "cat", 'c') => 0; + // try reading the attrs + uint8_t rbuf[256]; + lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => 0; + lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => 0; + lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => 0; + } + + lfs3_unmount(&lfs3) => 0; +''' + # test that wronly attrs are not read from disk [cases.test_attrs_fattr_wronly] defines.MODE = ['LFS3_A_WRONLY'] diff --git a/tests/test_kv.toml b/tests/test_kv.toml index ba1b581f..890a76c9 100644 --- a/tests/test_kv.toml +++ b/tests/test_kv.toml @@ -287,6 +287,178 @@ code = ''' lfs3_unmount(&lfs3) => 0; ''' +# test setting a file to zero size +# +# this is _not_ the same as removing +# +[cases.test_kv_set_zero] +# zero based on this mask +defines.MASK = 'range(0x8)' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + // create some kv files + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_set(&lfs3, "a", a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_set(&lfs3, "b", b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_set(&lfs3, "c", c, strlen(c)) => 0; + + // set zero some kv files + if (MASK & 0x1) { + lfs3_set(&lfs3, "a", (const uint8_t*)1, 0) => 0; + } + if (MASK & 0x2) { + lfs3_set(&lfs3, "b", (const uint8_t*)1, 0) => 0; + } + if (MASK & 0x4) { + lfs3_set(&lfs3, "c", (const uint8_t*)1, 0) => 0; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the file sizes + if (MASK & 0x1) { + lfs3_size(&lfs3, "a") => 0; + } else { + lfs3_size(&lfs3, "a") => strlen(a); + } + if (MASK & 0x2) { + lfs3_size(&lfs3, "b") => 0; + } else { + lfs3_size(&lfs3, "b") => strlen(b); + } + if (MASK & 0x4) { + lfs3_size(&lfs3, "c") => 0; + } else { + lfs3_size(&lfs3, "c") => strlen(c); + } + + // try reading the full files + uint8_t rbuf[256]; + if (MASK & 0x1) { + lfs3_get(&lfs3, "a", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "a", rbuf, sizeof(rbuf)) + => strlen(a); + assert(memcmp(rbuf, a, strlen(a)) == 0); + } + if (MASK & 0x2) { + lfs3_get(&lfs3, "b", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "b", rbuf, sizeof(rbuf)) + => strlen(b); + assert(memcmp(rbuf, b, strlen(b)) == 0); + } + if (MASK & 0x4) { + lfs3_get(&lfs3, "c", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "c", rbuf, sizeof(rbuf)) + => strlen(c); + assert(memcmp(rbuf, c, strlen(c)) == 0); + } + } + + lfs3_unmount(&lfs3) => 0; +''' + +# test setting a file to zero size + null +# +# this can trip up naive internal logic +# +[cases.test_kv_set_null] +# zero based on this mask +defines.MASK = 'range(0x8)' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + + // create some kv files + const char *a = "One 18.25 ounce package chocolate cake mix."; + lfs3_set(&lfs3, "a", a, strlen(a)) => 0; + const char *b = "One can prepared coconut pecan frosting."; + lfs3_set(&lfs3, "b", b, strlen(b)) => 0; + const char *c = "Three slash four cup vegetable oil."; + lfs3_set(&lfs3, "c", c, strlen(c)) => 0; + + // set zero some kv files + if (MASK & 0x1) { + lfs3_set(&lfs3, "a", NULL, 0) => 0; + } + if (MASK & 0x2) { + lfs3_set(&lfs3, "b", NULL, 0) => 0; + } + if (MASK & 0x4) { + lfs3_set(&lfs3, "c", NULL, 0) => 0; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfs3_unmount(&lfs3) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + } + + // try getting the file sizes + if (MASK & 0x1) { + lfs3_size(&lfs3, "a") => 0; + } else { + lfs3_size(&lfs3, "a") => strlen(a); + } + if (MASK & 0x2) { + lfs3_size(&lfs3, "b") => 0; + } else { + lfs3_size(&lfs3, "b") => strlen(b); + } + if (MASK & 0x4) { + lfs3_size(&lfs3, "c") => 0; + } else { + lfs3_size(&lfs3, "c") => strlen(c); + } + + // try reading the full files + uint8_t rbuf[256]; + if (MASK & 0x1) { + lfs3_get(&lfs3, "a", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "a", rbuf, sizeof(rbuf)) + => strlen(a); + assert(memcmp(rbuf, a, strlen(a)) == 0); + } + if (MASK & 0x2) { + lfs3_get(&lfs3, "b", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "b", rbuf, sizeof(rbuf)) + => strlen(b); + assert(memcmp(rbuf, b, strlen(b)) == 0); + } + if (MASK & 0x4) { + lfs3_get(&lfs3, "c", rbuf, sizeof(rbuf)) + => 0; + } else { + lfs3_get(&lfs3, "c", rbuf, sizeof(rbuf)) + => strlen(c); + assert(memcmp(rbuf, c, strlen(c)) == 0); + } + } + + lfs3_unmount(&lfs3) => 0; +''' + # test creating a bunch of kv files [cases.test_kv_many] defines.N = [40, 400]