Skip to content

Commit 2f2f4a1

Browse files
committed
Squashed 'littlefs/' changes from 0494ce7..8ed63b2
8ed63b2 Merge pull request #1084 from elupus/fix/packing a666730 Merge pull request #1078 from BrianPugh/unit-test-readme 47e738b Merge pull request #1071 from RocLoong/patch-1 81b0db0 Merge pull request #1070 from Noxet/filebd-wrong-cast 63ab1ff Merge pull request #1068 from littlefs-project/fix-dir-remove-read ca1081e Merge pull request #1065 from amubiera/fix-unsafe-use-of-bool 76027f1 Merge pull request #1064 from tim-nordell-nimbelink/fix/script_syntax_warnings 61a1b0b Tweaked lfs_gstate_iszero for terseness ffafb9c fix: avoid assuming struct packing 5281a20 README.md: Tweaked testing documentation f555203 Add a little bit of documentation on how to run tests. 936919d LFS_TRACE: Fixed sign mismatch in lfs_file_size d2c3a47 gha: Added test-yes-trace build/test job to CI 0320e7d Changed cast to correct type when trace is enabled for filebd caba4f3 Fixed dir iteration being broken by concurrent removes 152d030 Fix for "unsafe use of type bool" warning when compiling with MSVC. 8d01895 scripts: Fixed several SyntaxWarning for python test helpers git-subtree-dir: littlefs git-subtree-split: 8ed63b27be79ab59ee1cd15a950ddd64e7a602f7
1 parent e320c43 commit 2f2f4a1

File tree

7 files changed

+173
-35
lines changed

7 files changed

+173
-35
lines changed

.github/workflows/test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,29 @@ jobs:
374374
run: |
375375
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
376376
377+
# run with all trace options enabled to at least make sure these
378+
# all compile
379+
test-yes-trace:
380+
runs-on: ubuntu-latest
381+
steps:
382+
- uses: actions/checkout@v4
383+
- name: install
384+
run: |
385+
# need a few things
386+
sudo apt-get update -qq
387+
sudo apt-get install -qq gcc python3 python3-pip
388+
pip3 install toml
389+
gcc --version
390+
python3 --version
391+
- name: test-yes-trace
392+
run: |
393+
CFLAGS="$CFLAGS \
394+
-DLFS_YES_TRACE \
395+
-DLFS_RAMBD_YES_TRACE \
396+
-DLFS_FILEBD_YES_TRACE \
397+
-DLFS_RAMBD_YES_TRACE" \
398+
make test
399+
377400
# run LFS_MULTIVERSION tests
378401
test-multiversion:
379402
runs-on: ubuntu-latest

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,47 @@ The tests assume a Linux environment and can be started with make:
199199
make test
200200
```
201201

202+
Tests are implemented in C in the .toml files found in the `tests` directory.
203+
When developing a feature or fixing a bug, it is frequently useful to run a
204+
single test case or suite of tests:
205+
206+
``` bash
207+
./scripts/test.py -l runners/test_runner # list available test suites
208+
./scripts/test.py -L runners/test_runner test_dirs # list available test cases
209+
./scripts/test.py runners/test_runner test_dirs # run a specific test suite
210+
```
211+
212+
If an assert fails in a test, test.py will try to print information about the
213+
failure:
214+
215+
``` bash
216+
tests/test_dirs.toml:1:failure: test_dirs_root:1g12gg2 (PROG_SIZE=16, ERASE_SIZE=512) failed
217+
tests/test_dirs.toml:5:assert: assert failed with 0, expected eq 42
218+
lfs_mount(&lfs, cfg) => 42;
219+
```
220+
221+
This includes the test id, which can be passed to test.py to run only that
222+
specific test permutation:
223+
224+
``` bash
225+
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 # run a specific test permutation
226+
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 --gdb # drop into gdb on failure
227+
```
228+
229+
Some other flags that may be useful:
230+
231+
```bash
232+
./scripts/test.py runners/test_runner -b -j # run tests in parallel
233+
./scripts/test.py runners/test_runner -v -O- # redirect stdout to stdout
234+
./scripts/test.py runners/test_runner -ddisk # capture resulting disk image
235+
```
236+
237+
See `-h/--help` for a full list of available flags:
238+
239+
``` bash
240+
./scripts/test.py --help
241+
```
242+
202243
## License
203244

204245
The littlefs is provided under the [BSD-3-Clause] license. See

bd/lfs_filebd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
133133

134134
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
135135
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
136-
(void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
136+
(void*)cfg, block, ((lfs_filebd_t*)cfg->context)->cfg->erase_size);
137137
lfs_filebd_t *bd = cfg->context;
138138

139139
// check if erase is valid

lfs.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -404,18 +404,15 @@ struct lfs_diskoff {
404404

405405
// operations on global state
406406
static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) {
407-
for (int i = 0; i < 3; i++) {
408-
((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
409-
}
407+
a->tag ^= b->tag;
408+
a->pair[0] ^= b->pair[0];
409+
a->pair[1] ^= b->pair[1];
410410
}
411411

412412
static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) {
413-
for (int i = 0; i < 3; i++) {
414-
if (((uint32_t*)a)[i] != 0) {
415-
return false;
416-
}
417-
}
418-
return true;
413+
return a->tag == 0
414+
&& a->pair[0] == 0
415+
&& a->pair[1] == 0;
419416
}
420417

421418
#ifndef LFS_READONLY
@@ -2369,7 +2366,8 @@ fixmlist:;
23692366
if (d->m.pair != pair) {
23702367
for (int i = 0; i < attrcount; i++) {
23712368
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
2372-
d->id == lfs_tag_id(attrs[i].tag)) {
2369+
d->id == lfs_tag_id(attrs[i].tag) &&
2370+
d->type != LFS_TYPE_DIR) {
23732371
d->m.pair[0] = LFS_BLOCK_NULL;
23742372
d->m.pair[1] = LFS_BLOCK_NULL;
23752373
} 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,
25582556
if (err != LFS_ERR_NOENT) {
25592557
if (lfs_gstate_hasorphans(&lfs->gstate)) {
25602558
// next step, clean up orphans
2561-
err = lfs_fs_preporphans(lfs, -hasparent);
2559+
err = lfs_fs_preporphans(lfs, -(int8_t)hasparent);
25622560
if (err) {
25632561
return err;
25642562
}
@@ -6288,7 +6286,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
62886286

62896287
lfs_soff_t res = lfs_file_size_(lfs, file);
62906288

6291-
LFS_TRACE("lfs_file_size -> %"PRId32, res);
6289+
LFS_TRACE("lfs_file_size -> %"PRIu32, res);
62926290
LFS_UNLOCK(lfs->cfg);
62936291
return res;
62946292
}

scripts/prettyasserts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@
3535
'assert': ['assert'],
3636
'arrow': ['=>'],
3737
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
38-
'paren': ['\(', '\)'],
38+
'paren': [r'\(', r'\)'],
3939
'cmp': CMP.keys(),
40-
'logic': ['\&\&', '\|\|'],
41-
'sep': [':', ';', '\{', '\}', ','],
40+
'logic': [r'\&\&', r'\|\|'],
41+
'sep': [':', ';', r'\{', r'\}', ','],
4242
'op': ['->'], # specifically ops that conflict with cmp
4343
}
4444

scripts/test.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ def parse_define(v):
102102
# the runner itself.
103103
for v_ in csplit(v):
104104
m = re.search(r'\brange\b\s*\('
105-
'(?P<start>[^,\s]*)'
106-
'\s*(?:,\s*(?P<stop>[^,\s]*)'
107-
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
105+
r'(?P<start>[^,\s]*)'
106+
r'\s*(?:,\s*(?P<stop>[^,\s]*)'
107+
r'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
108108
v_)
109109
if m:
110110
start = (int(m.group('start'), 0)
@@ -163,8 +163,8 @@ def __init__(self, path, args={}):
163163
code_linenos = []
164164
for i, line in enumerate(f):
165165
match = re.match(
166-
'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
167-
'|' '(?P<code>code\s*=)',
166+
r'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
167+
r'|' r'(?P<code>code\s*=)',
168168
line)
169169
if match and match.group('case'):
170170
case_linenos.append((i+1, match.group('name')))
@@ -602,9 +602,9 @@ def find_perms(runner_, ids=[], **args):
602602
errors='replace',
603603
close_fds=False)
604604
pattern = re.compile(
605-
'^(?P<case>[^\s]+)'
606-
'\s+(?P<flags>[^\s]+)'
607-
'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
605+
r'^(?P<case>[^\s]+)'
606+
r'\s+(?P<flags>[^\s]+)'
607+
r'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
608608
# skip the first line
609609
for line in it.islice(proc.stdout, 1, None):
610610
m = pattern.match(line)
@@ -632,8 +632,8 @@ def find_perms(runner_, ids=[], **args):
632632
errors='replace',
633633
close_fds=False)
634634
pattern = re.compile(
635-
'^(?P<case>[^\s]+)'
636-
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
635+
r'^(?P<case>[^\s]+)'
636+
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
637637
# skip the first line
638638
for line in it.islice(proc.stdout, 1, None):
639639
m = pattern.match(line)
@@ -676,8 +676,8 @@ def find_path(runner_, id, **args):
676676
errors='replace',
677677
close_fds=False)
678678
pattern = re.compile(
679-
'^(?P<case>[^\s]+)'
680-
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
679+
r'^(?P<case>[^\s]+)'
680+
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
681681
# skip the first line
682682
for line in it.islice(proc.stdout, 1, None):
683683
m = pattern.match(line)
@@ -706,7 +706,7 @@ def find_defines(runner_, id, **args):
706706
errors='replace',
707707
close_fds=False)
708708
defines = co.OrderedDict()
709-
pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)')
709+
pattern = re.compile(r'^(?P<define>\w+)=(?P<value>.+)')
710710
for line in proc.stdout:
711711
m = pattern.match(line)
712712
if m:
@@ -781,12 +781,12 @@ def run_stage(name, runner_, ids, stdout_, trace_, output_, **args):
781781
failures = []
782782
killed = False
783783

784-
pattern = re.compile('^(?:'
785-
'(?P<op>running|finished|skipped|powerloss) '
786-
'(?P<id>(?P<case>[^:]+)[^\s]*)'
787-
'|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
788-
' *(?P<message>.*)'
789-
')$')
784+
pattern = re.compile(r'^(?:'
785+
r'(?P<op>running|finished|skipped|powerloss) '
786+
r'(?P<id>(?P<case>[^:]+)[^\s]*)'
787+
r'|' r'(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
788+
r' *(?P<message>.*)'
789+
r')$')
790790
locals = th.local()
791791
children = set()
792792

tests/test_dirs.toml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,82 @@ code = '''
725725
lfs_unmount(&lfs) => 0;
726726
'''
727727

728+
[cases.test_dirs_remove_read]
729+
defines.N = 10
730+
if = 'N < BLOCK_COUNT/2'
731+
code = '''
732+
lfs_t lfs;
733+
lfs_format(&lfs, cfg) => 0;
734+
lfs_mount(&lfs, cfg) => 0;
735+
lfs_mkdir(&lfs, "prickly-pear") => 0;
736+
for (int i = 0; i < N; i++) {
737+
char path[1024];
738+
sprintf(path, "prickly-pear/cactus%03d", i);
739+
lfs_mkdir(&lfs, path) => 0;
740+
}
741+
lfs_dir_t dir;
742+
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
743+
struct lfs_info info;
744+
lfs_dir_read(&lfs, &dir, &info) => 1;
745+
assert(info.type == LFS_TYPE_DIR);
746+
assert(strcmp(info.name, ".") == 0);
747+
lfs_dir_read(&lfs, &dir, &info) => 1;
748+
assert(info.type == LFS_TYPE_DIR);
749+
assert(strcmp(info.name, "..") == 0);
750+
for (int i = 0; i < N; i++) {
751+
char path[1024];
752+
sprintf(path, "cactus%03d", i);
753+
lfs_dir_read(&lfs, &dir, &info) => 1;
754+
assert(info.type == LFS_TYPE_DIR);
755+
assert(strcmp(info.name, path) == 0);
756+
}
757+
lfs_dir_read(&lfs, &dir, &info) => 0;
758+
lfs_dir_close(&lfs, &dir) => 0;
759+
lfs_unmount(&lfs);
760+
761+
for (lfs_size_t k = 0; k < N; k++) {
762+
for (lfs_size_t j = 0; j < N; j++) {
763+
lfs_mount(&lfs, cfg) => 0;
764+
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
765+
lfs_dir_read(&lfs, &dir, &info) => 1;
766+
assert(info.type == LFS_TYPE_DIR);
767+
assert(strcmp(info.name, ".") == 0);
768+
lfs_dir_read(&lfs, &dir, &info) => 1;
769+
assert(info.type == LFS_TYPE_DIR);
770+
assert(strcmp(info.name, "..") == 0);
771+
// iterate over dirs < j
772+
for (unsigned i = 0; i < j; i++) {
773+
char path[1024];
774+
sprintf(path, "cactus%03d", i);
775+
lfs_dir_read(&lfs, &dir, &info) => 1;
776+
assert(info.type == LFS_TYPE_DIR);
777+
assert(strcmp(info.name, path) == 0);
778+
}
779+
780+
// remove k while iterating
781+
char path[1024];
782+
sprintf(path, "prickly-pear/cactus%03d", k);
783+
lfs_remove(&lfs, path) => 0;
784+
785+
// iterate over dirs >= j
786+
for (unsigned i = j; i < ((k >= j) ? N-1 : N); i++) {
787+
char path[1024];
788+
sprintf(path, "cactus%03d", (k >= j && i >= k) ? i+1 : i);
789+
lfs_dir_read(&lfs, &dir, &info) => 1;
790+
assert(info.type == LFS_TYPE_DIR);
791+
assert(strcmp(info.name, path) == 0);
792+
}
793+
lfs_dir_read(&lfs, &dir, &info) => 0;
794+
lfs_dir_close(&lfs, &dir) => 0;
795+
796+
// recreate k
797+
sprintf(path, "prickly-pear/cactus%03d", k);
798+
lfs_mkdir(&lfs, path) => 0;
799+
lfs_unmount(&lfs) => 0;
800+
}
801+
}
802+
'''
803+
728804
[cases.test_dirs_other_errors]
729805
code = '''
730806
lfs_t lfs;

0 commit comments

Comments
 (0)