mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-11-16 12:34:34 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ed63b27be | ||
|
|
a666730044 | ||
|
|
47e738b788 | ||
|
|
81b0db0cdc | ||
|
|
63ab1ffb65 | ||
|
|
ca1081e7c4 | ||
|
|
76027f1502 | ||
|
|
61a1b0b496 | ||
|
|
ffafb9cbb1 | ||
|
|
5281a20f6c | ||
|
|
f55520380d | ||
|
|
936919d134 | ||
|
|
d2c3a47627 | ||
|
|
0320e7db0e | ||
|
|
caba4f31df | ||
|
|
152d03043c | ||
|
|
8d01895b32 |
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
@@ -374,6 +374,29 @@ jobs:
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
|
||||
|
||||
# run with all trace options enabled to at least make sure these
|
||||
# all compile
|
||||
test-yes-trace:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc python3 python3-pip
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
- name: test-yes-trace
|
||||
run: |
|
||||
CFLAGS="$CFLAGS \
|
||||
-DLFS_YES_TRACE \
|
||||
-DLFS_RAMBD_YES_TRACE \
|
||||
-DLFS_FILEBD_YES_TRACE \
|
||||
-DLFS_RAMBD_YES_TRACE" \
|
||||
make test
|
||||
|
||||
# run LFS_MULTIVERSION tests
|
||||
test-multiversion:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
41
README.md
41
README.md
@@ -199,6 +199,47 @@ The tests assume a Linux environment and can be started with make:
|
||||
make test
|
||||
```
|
||||
|
||||
Tests are implemented in C in the .toml files found in the `tests` directory.
|
||||
When developing a feature or fixing a bug, it is frequently useful to run a
|
||||
single test case or suite of tests:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py -l runners/test_runner # list available test suites
|
||||
./scripts/test.py -L runners/test_runner test_dirs # list available test cases
|
||||
./scripts/test.py runners/test_runner test_dirs # run a specific test suite
|
||||
```
|
||||
|
||||
If an assert fails in a test, test.py will try to print information about the
|
||||
failure:
|
||||
|
||||
``` bash
|
||||
tests/test_dirs.toml:1:failure: test_dirs_root:1g12gg2 (PROG_SIZE=16, ERASE_SIZE=512) failed
|
||||
tests/test_dirs.toml:5:assert: assert failed with 0, expected eq 42
|
||||
lfs_mount(&lfs, cfg) => 42;
|
||||
```
|
||||
|
||||
This includes the test id, which can be passed to test.py to run only that
|
||||
specific test permutation:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 # run a specific test permutation
|
||||
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 --gdb # drop into gdb on failure
|
||||
```
|
||||
|
||||
Some other flags that may be useful:
|
||||
|
||||
```bash
|
||||
./scripts/test.py runners/test_runner -b -j # run tests in parallel
|
||||
./scripts/test.py runners/test_runner -v -O- # redirect stdout to stdout
|
||||
./scripts/test.py runners/test_runner -ddisk # capture resulting disk image
|
||||
```
|
||||
|
||||
See `-h/--help` for a full list of available flags:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py --help
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The littlefs is provided under the [BSD-3-Clause] license. See
|
||||
|
||||
@@ -133,7 +133,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
|
||||
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
|
||||
(void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
|
||||
(void*)cfg, block, ((lfs_filebd_t*)cfg->context)->cfg->erase_size);
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
|
||||
// check if erase is valid
|
||||
|
||||
22
lfs.c
22
lfs.c
@@ -404,18 +404,15 @@ struct lfs_diskoff {
|
||||
|
||||
// operations on global state
|
||||
static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
|
||||
}
|
||||
a->tag ^= b->tag;
|
||||
a->pair[0] ^= b->pair[0];
|
||||
a->pair[1] ^= b->pair[1];
|
||||
}
|
||||
|
||||
static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (((uint32_t*)a)[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return a->tag == 0
|
||||
&& a->pair[0] == 0
|
||||
&& a->pair[1] == 0;
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
@@ -2369,7 +2366,8 @@ fixmlist:;
|
||||
if (d->m.pair != pair) {
|
||||
for (int i = 0; i < attrcount; i++) {
|
||||
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||
d->id == lfs_tag_id(attrs[i].tag)) {
|
||||
d->id == lfs_tag_id(attrs[i].tag) &&
|
||||
d->type != LFS_TYPE_DIR) {
|
||||
d->m.pair[0] = LFS_BLOCK_NULL;
|
||||
d->m.pair[1] = LFS_BLOCK_NULL;
|
||||
} else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||
@@ -2558,7 +2556,7 @@ static int lfs_dir_orphaningcommit(lfs_t *lfs, lfs_mdir_t *dir,
|
||||
if (err != LFS_ERR_NOENT) {
|
||||
if (lfs_gstate_hasorphans(&lfs->gstate)) {
|
||||
// next step, clean up orphans
|
||||
err = lfs_fs_preporphans(lfs, -hasparent);
|
||||
err = lfs_fs_preporphans(lfs, -(int8_t)hasparent);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -6288,7 +6286,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
|
||||
|
||||
lfs_soff_t res = lfs_file_size_(lfs, file);
|
||||
|
||||
LFS_TRACE("lfs_file_size -> %"PRId32, res);
|
||||
LFS_TRACE("lfs_file_size -> %"PRIu32, res);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ LEXEMES = {
|
||||
'assert': ['assert'],
|
||||
'arrow': ['=>'],
|
||||
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
|
||||
'paren': ['\(', '\)'],
|
||||
'paren': [r'\(', r'\)'],
|
||||
'cmp': CMP.keys(),
|
||||
'logic': ['\&\&', '\|\|'],
|
||||
'sep': [':', ';', '\{', '\}', ','],
|
||||
'logic': [r'\&\&', r'\|\|'],
|
||||
'sep': [':', ';', r'\{', r'\}', ','],
|
||||
'op': ['->'], # specifically ops that conflict with cmp
|
||||
}
|
||||
|
||||
|
||||
@@ -102,9 +102,9 @@ class TestCase:
|
||||
# the runner itself.
|
||||
for v_ in csplit(v):
|
||||
m = re.search(r'\brange\b\s*\('
|
||||
'(?P<start>[^,\s]*)'
|
||||
'\s*(?:,\s*(?P<stop>[^,\s]*)'
|
||||
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
|
||||
r'(?P<start>[^,\s]*)'
|
||||
r'\s*(?:,\s*(?P<stop>[^,\s]*)'
|
||||
r'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
|
||||
v_)
|
||||
if m:
|
||||
start = (int(m.group('start'), 0)
|
||||
@@ -163,8 +163,8 @@ class TestSuite:
|
||||
code_linenos = []
|
||||
for i, line in enumerate(f):
|
||||
match = re.match(
|
||||
'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
|
||||
'|' '(?P<code>code\s*=)',
|
||||
r'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
|
||||
r'|' r'(?P<code>code\s*=)',
|
||||
line)
|
||||
if match and match.group('case'):
|
||||
case_linenos.append((i+1, match.group('name')))
|
||||
@@ -602,9 +602,9 @@ def find_perms(runner_, ids=[], **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<flags>[^\s]+)'
|
||||
'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<flags>[^\s]+)'
|
||||
r'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -632,8 +632,8 @@ def find_perms(runner_, ids=[], **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -676,8 +676,8 @@ def find_path(runner_, id, **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -706,7 +706,7 @@ def find_defines(runner_, id, **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
defines = co.OrderedDict()
|
||||
pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)')
|
||||
pattern = re.compile(r'^(?P<define>\w+)=(?P<value>.+)')
|
||||
for line in proc.stdout:
|
||||
m = pattern.match(line)
|
||||
if m:
|
||||
@@ -781,12 +781,12 @@ def run_stage(name, runner_, ids, stdout_, trace_, output_, **args):
|
||||
failures = []
|
||||
killed = False
|
||||
|
||||
pattern = re.compile('^(?:'
|
||||
'(?P<op>running|finished|skipped|powerloss) '
|
||||
'(?P<id>(?P<case>[^:]+)[^\s]*)'
|
||||
'|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
|
||||
' *(?P<message>.*)'
|
||||
')$')
|
||||
pattern = re.compile(r'^(?:'
|
||||
r'(?P<op>running|finished|skipped|powerloss) '
|
||||
r'(?P<id>(?P<case>[^:]+)[^\s]*)'
|
||||
r'|' r'(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
|
||||
r' *(?P<message>.*)'
|
||||
r')$')
|
||||
locals = th.local()
|
||||
children = set()
|
||||
|
||||
|
||||
@@ -725,6 +725,82 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_dirs_remove_read]
|
||||
defines.N = 10
|
||||
if = 'N < BLOCK_COUNT/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mkdir(&lfs, "prickly-pear") => 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "prickly-pear/cactus%03d", i);
|
||||
lfs_mkdir(&lfs, path) => 0;
|
||||
}
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
|
||||
struct lfs_info info;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
for (int i = 0; i < N; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs);
|
||||
|
||||
for (lfs_size_t k = 0; k < N; k++) {
|
||||
for (lfs_size_t j = 0; j < N; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
// iterate over dirs < j
|
||||
for (unsigned i = 0; i < j; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
|
||||
// remove k while iterating
|
||||
char path[1024];
|
||||
sprintf(path, "prickly-pear/cactus%03d", k);
|
||||
lfs_remove(&lfs, path) => 0;
|
||||
|
||||
// iterate over dirs >= j
|
||||
for (unsigned i = j; i < ((k >= j) ? N-1 : N); i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", (k >= j && i >= k) ? i+1 : i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
// recreate k
|
||||
sprintf(path, "prickly-pear/cactus%03d", k);
|
||||
lfs_mkdir(&lfs, path) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
[cases.test_dirs_other_errors]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
|
||||
Reference in New Issue
Block a user