diff options
-rw-r--r-- | Makefile | 36 | ||||
-rw-r--r-- | runners/test_runner.c | 22 | ||||
-rw-r--r-- | runners/test_runner.h | 6 | ||||
-rwxr-xr-x | scripts/test.py | 1759 | ||||
-rwxr-xr-x | scripts/test_.py | 1027 | ||||
-rw-r--r-- | tests/test_alloc.toml | 292 | ||||
-rw-r--r-- | tests/test_attrs.toml | 50 | ||||
-rw-r--r-- | tests/test_badblocks.toml | 129 | ||||
-rw-r--r-- | tests/test_dirs.toml | 267 | ||||
-rw-r--r-- | tests/test_entries.toml | 87 | ||||
-rw-r--r-- | tests/test_evil.toml | 116 | ||||
-rw-r--r-- | tests/test_exhaustion.toml | 204 | ||||
-rw-r--r-- | tests/test_files.toml | 174 | ||||
-rw-r--r-- | tests/test_interspersed.toml | 72 | ||||
-rw-r--r-- | tests/test_move.toml | 458 | ||||
-rw-r--r-- | tests/test_orphans.toml | 61 | ||||
-rw-r--r-- | tests/test_paths.toml | 121 | ||||
-rw-r--r-- | tests/test_relocations.toml | 162 | ||||
-rw-r--r-- | tests/test_seek.toml | 97 | ||||
-rw-r--r-- | tests/test_superblocks.toml | 86 | ||||
-rw-r--r-- | tests/test_truncate.toml | 154 |
21 files changed, 2550 insertions, 2830 deletions
@@ -32,7 +32,7 @@ DEP := $(SRC:%.c=$(BUILDDIR)%.d) ASM := $(SRC:%.c=$(BUILDDIR)%.s) CGI := $(SRC:%.c=$(BUILDDIR)%.ci) -TESTS ?= $(wildcard tests_/*.toml) +TESTS ?= $(wildcard tests/*.toml) TEST_SRC ?= $(SRC) \ $(filter-out $(wildcard bd/*.*.c),$(wildcard bd/*.c)) \ runners/test_runner.c @@ -53,10 +53,11 @@ override CFLAGS += -g3 override CFLAGS += -I. override CFLAGS += -std=c99 -Wall -pedantic override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef +override CFLAGS += -ftrack-macro-expansion=0 -override TESTFLAGS_ += -b +override TESTFLAGS += -b # forward -j flag -override TESTFLAGS_ += $(filter -j%,$(MAKEFLAGS)) +override TESTFLAGS += $(filter -j%,$(MAKEFLAGS)) ifdef VERBOSE override TESTFLAGS += -v override CALLSFLAGS += -v @@ -65,15 +66,14 @@ override DATAFLAGS += -v override STACKFLAGS += -v override STRUCTSFLAGS += -v override COVERAGEFLAGS += -v -override TESTFLAGS_ += -v -override TESTCFLAGS_ += -v +override TESTFLAGS += -v +override TESTCFLAGS += -v endif ifdef EXEC -override TESTFLAGS_ += --exec="$(EXEC)" +override TESTFLAGS += --exec="$(EXEC)" endif ifdef COVERAGE -override TESTFLAGS += --coverage -override TESTFLAGS_ += --coverage +override TESTFLAGS += --coverage endif ifdef BUILDDIR override TESTFLAGS += --build-dir="$(BUILDDIR:/=)" @@ -112,23 +112,16 @@ tags: calls: $(CGI) ./scripts/calls.py $^ $(CALLSFLAGS) -.PHONY: test -test: - ./scripts/test.py $(TESTFLAGS) -.SECONDEXPANSION: -test%: tests/test$$(firstword $$(subst \#, ,%)).toml - ./scripts/test.py $@ $(TESTFLAGS) - .PHONY: test_runner test_runner: $(BUILDDIR)runners/test_runner -.PHONY: test_ -test_: test_runner - ./scripts/test_.py --runner=$(BUILDDIR)runners/test_runner $(TESTFLAGS_) +.PHONY: test +test: test_runner + ./scripts/test.py --runner=$(BUILDDIR)runners/test_runner $(TESTFLAGS) .PHONY: test_list test_list: test_runner - ./scripts/test_.py --runner=$(BUILDDIR)runners/test_runner $(TESTFLAGS_) -l + ./scripts/test.py --runner=$(BUILDDIR)runners/test_runner $(TESTFLAGS) -l .PHONY: code code: $(OBJ) @@ -199,10 +192,10 @@ $(BUILDDIR)%.a.c: $(BUILDDIR)%.c ./scripts/explode_asserts.py $< -o $@ $(BUILDDIR)%.t.c: %.toml - ./scripts/test_.py -c $< $(TESTCFLAGS_) -o $@ + ./scripts/test.py -c $< $(TESTCFLAGS) -o $@ $(BUILDDIR)%.t.c: %.c $(TESTS) - ./scripts/test_.py -c $(TESTS) -s $< $(TESTCFLAGS_) -o $@ + ./scripts/test.py -c $(TESTS) -s $< $(TESTCFLAGS) -o $@ # clean everything .PHONY: clean @@ -215,7 +208,6 @@ clean: rm -f $(CGI) rm -f $(DEP) rm -f $(ASM) - rm -f $(BUILDDIR)tests/*.toml.* rm -f $(TEST_TSRC) rm -f $(TEST_TASRC) rm -f $(TEST_TAOBJ) diff --git a/runners/test_runner.c b/runners/test_runner.c index 1b3ea5c..21ff1c0 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -10,17 +10,17 @@ // test geometries struct test_geometry { const char *name; - uintmax_t defines[TEST_GEOMETRY_DEFINE_COUNT]; + intmax_t defines[TEST_GEOMETRY_DEFINE_COUNT]; }; const struct test_geometry test_geometries[TEST_GEOMETRY_COUNT] = TEST_GEOMETRIES; // test define lookup and management -const uintmax_t *test_override_defines; -uintmax_t (*const *test_case_defines)(void); -const uintmax_t *test_geometry_defines; -const uintmax_t test_default_defines[TEST_PREDEFINE_COUNT] +const intmax_t *test_override_defines; +intmax_t (*const *test_case_defines)(void); +const intmax_t *test_geometry_defines; +const intmax_t test_default_defines[TEST_PREDEFINE_COUNT] = TEST_DEFAULTS; uint8_t test_override_predefine_map[TEST_PREDEFINE_COUNT]; @@ -37,7 +37,7 @@ const char *const *test_define_names; size_t test_define_count; -uintmax_t test_predefine(size_t define) { +intmax_t test_predefine(size_t define) { if (test_override_defines && test_override_predefine_map[define] != 0xff) { return test_override_defines[test_override_predefine_map[define]]; @@ -52,7 +52,7 @@ uintmax_t test_predefine(size_t define) { } } -uintmax_t test_define(size_t define) { +intmax_t test_define(size_t define) { if (test_override_defines && test_override_define_map[define] != 0xff) { return test_override_defines[test_override_define_map[define]]; @@ -73,7 +73,7 @@ static void test_define_geometry(const struct test_geometry *geometry) { static void test_define_overrides( const char *const *override_names, - const uintmax_t *override_defines, + const intmax_t *override_defines, size_t override_count) { test_override_defines = override_defines; test_override_names = override_names; @@ -583,7 +583,7 @@ int main(int argc, char **argv) { void (*op)(void) = run; static const char **override_names = NULL; - static uintmax_t *override_defines = NULL; + static intmax_t *override_defines = NULL; static size_t override_count = 0; static size_t override_cap = 0; @@ -685,10 +685,10 @@ int main(int argc, char **argv) { override_names = realloc(override_names, override_cap * sizeof(const char *)); override_defines = realloc(override_defines, override_cap - * sizeof(uintmax_t)); + * sizeof(intmax_t)); } - // parse into string key/uintmax_t value, cannibalizing the + // parse into string key/intmax_t value, cannibalizing the // arg in the process char *sep = strchr(optarg, '='); char *parsed = NULL; diff --git a/runners/test_runner.h b/runners/test_runner.h index 22cbcaa..e033637 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -19,7 +19,7 @@ struct test_case { test_types_t types; size_t permutations; - uintmax_t (*const *const *defines)(void); + intmax_t (*const *const *defines)(void); bool (*filter)(void); void (*run)(struct lfs_config *cfg); @@ -43,8 +43,8 @@ extern const size_t test_suite_count; // access generated test defines -uintmax_t test_predefine(size_t define); -uintmax_t test_define(size_t define); +intmax_t test_predefine(size_t define); +intmax_t test_define(size_t define); // a few preconfigured defines that control how tests run #define READ_SIZE test_predefine(0) diff --git a/scripts/test.py b/scripts/test.py index 92a13b1..4110bba 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -1,860 +1,1027 @@ #!/usr/bin/env python3 - -# This script manages littlefs tests, which are configured with -# .toml files stored in the tests directory. +# +# Script to compile and runs tests. # -import toml +import collections as co +import errno import glob -import re -import os -import io import itertools as it -import collections.abc as abc -import subprocess as sp -import base64 -import sys -import copy -import shlex +import math as m +import os import pty -import errno +import re +import shlex +import shutil import signal +import subprocess as sp +import threading as th +import time +import toml -TEST_PATHS = 'tests' -RULES = """ -# add block devices to sources -TESTSRC ?= $(SRC) $(wildcard bd/*.c) - -define FLATTEN -%(path)s%%$(subst /,.,$(target)): $(target) - ./scripts/explode_asserts.py $$< -o $$@ -endef -$(foreach target,$(TESTSRC),$(eval $(FLATTEN))) - --include %(path)s*.d -.SECONDARY: -%(path)s.test: %(path)s.test.o \\ - $(foreach t,$(subst /,.,$(TESTSRC:.c=.o)),%(path)s.$t) - $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ +TEST_PATHS = ['tests'] +RUNNER_PATH = './runners/test_runner' -# needed in case builddir is different -%(path)s%%.o: %(path)s%%.c - $(CC) -c -MMD $(CFLAGS) $< -o $@ -""" -COVERAGE_RULES = """ -%(path)s.test: override CFLAGS += -fprofile-arcs -ftest-coverage - -# delete lingering coverage -%(path)s.test: | %(path)s.info.clean -.PHONY: %(path)s.info.clean -%(path)s.info.clean: - rm -f %(path)s*.gcda - -# accumulate coverage info -.PHONY: %(path)s.info -%(path)s.info: - $(strip $(LCOV) -c \\ - $(addprefix -d ,$(wildcard %(path)s*.gcda)) \\ - --rc 'geninfo_adjust_src_path=$(shell pwd)' \\ - -o $@) - $(LCOV) -e $@ $(addprefix /,$(SRC)) -o $@ -ifdef COVERAGETARGET - $(strip $(LCOV) -a $@ \\ - $(addprefix -a ,$(wildcard $(COVERAGETARGET))) \\ - -o $(COVERAGETARGET)) -endif -""" -GLOBALS = """ -//////////////// AUTOGENERATED TEST //////////////// -#include "lfs.h" +SUITE_PROLOGUE = """ +#include "runners/test_runner.h" #include "bd/lfs_testbd.h" #include <stdio.h> -extern const char *lfs_testbd_path; -extern uint32_t lfs_testbd_cycles; """ -DEFINES = { - 'LFS_READ_SIZE': 16, - 'LFS_PROG_SIZE': 'LFS_READ_SIZE', - 'LFS_BLOCK_SIZE': 512, - 'LFS_BLOCK_COUNT': 1024, - 'LFS_BLOCK_CYCLES': -1, - 'LFS_CACHE_SIZE': '(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)', - 'LFS_LOOKAHEAD_SIZE': 16, - 'LFS_ERASE_VALUE': 0xff, - 'LFS_ERASE_CYCLES': 0, - 'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR', -} -PROLOGUE = """ - // prologue - __attribute__((unused)) lfs_t lfs; - __attribute__((unused)) lfs_testbd_t bd; - __attribute__((unused)) lfs_file_t file; - __attribute__((unused)) lfs_dir_t dir; - __attribute__((unused)) struct lfs_info info; - __attribute__((unused)) char path[1024]; - __attribute__((unused)) uint8_t buffer[1024]; - __attribute__((unused)) lfs_size_t size; - __attribute__((unused)) int err; - - __attribute__((unused)) const struct lfs_config cfg = { - .context = &bd, - .read = lfs_testbd_read, - .prog = lfs_testbd_prog, - .erase = lfs_testbd_erase, - .sync = lfs_testbd_sync, - .read_size = LFS_READ_SIZE, - .prog_size = LFS_PROG_SIZE, - .block_size = LFS_BLOCK_SIZE, - .block_count = LFS_BLOCK_COUNT, - .block_cycles = LFS_BLOCK_CYCLES, - .cache_size = LFS_CACHE_SIZE, - .lookahead_size = LFS_LOOKAHEAD_SIZE, - }; - - __attribute__((unused)) const struct lfs_testbd_config bdcfg = { - .erase_value = LFS_ERASE_VALUE, - .erase_cycles = LFS_ERASE_CYCLES, - .badblock_behavior = LFS_BADBLOCK_BEHAVIOR, - .power_cycles = lfs_testbd_cycles, - }; - - lfs_testbd_createcfg(&cfg, lfs_testbd_path, &bdcfg) => 0; +CASE_PROLOGUE = """ """ -EPILOGUE = """ - // epilogue - lfs_testbd_destroy(&cfg) => 0; +CASE_EPILOGUE = """ """ -PASS = '\033[32m✓\033[0m' -FAIL = '\033[31m✗\033[0m' -class TestFailure(Exception): - def __init__(self, case, returncode=None, stdout=None, assert_=None): - self.case = case - self.returncode = returncode - self.stdout = stdout - self.assert_ = assert_ -class TestCase: - def __init__(self, config, filter=filter, - suite=None, caseno=None, lineno=None, **_): - self.config = config - self.filter = filter - self.suite = suite - self.caseno = caseno - self.lineno = lineno - - self.code = config['code'] - self.code_lineno = config['code_lineno'] - self.defines = config.get('define', {}) - self.if_ = config.get('if', None) - self.in_ = config.get('in', None) - - self.result = None - - def __str__(self): - if hasattr(self, 'permno'): - if any(k not in self.case.defines for k in self.defines): - return '%s#%d#%d (%s)' % ( - self.suite.name, self.caseno, self.permno, ', '.join( - '%s=%s' % (k, v) for k, v in self.defines.items() - if k not in self.case.defines)) - else: - return '%s#%d#%d' % ( - self.suite.name, self.caseno, self.permno) - else: - return '%s#%d' % ( - self.suite.name, self.caseno) - - def permute(self, class_=None, defines={}, permno=None, **_): - ncase = (class_ or type(self))(self.config) - for k, v in self.__dict__.items(): - setattr(ncase, k, v) - ncase.case = self - ncase.perms = [ncase] - ncase.permno = permno - ncase.defines = defines - return ncase - - def build(self, f, **_): - # prologue - for k, v in sorted(self.defines.items()): - if k not in self.suite.defines: - f.write('#define %s %s\n' % (k, v)) - - f.write('void test_case%d(%s) {' % (self.caseno, ','.join( - '\n'+8*' '+'__attribute__((unused)) intmax_t %s' % k - for k in sorted(self.perms[0].defines) - if k not in self.defines))) - - f.write(PROLOGUE) - f.write('\n') - f.write(4*' '+'// test case %d\n' % self.caseno) - f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path)) - - # test case goes here - f.write(self.code) - - # epilogue - f.write(EPILOGUE) - f.write('}\n') - - for k, v in sorted(self.defines.items()): - if k not in self.suite.defines: - f.write('#undef %s\n' % k) - - def shouldtest(self, **args): - if (self.filter is not None and - len(self.filter) >= 1 and - self.filter[0] != self.caseno): - return False - elif (self.filter is not None and - len(self.filter) >= 2 and - self.filter[1] != self.permno): - return False - elif args.get('no_internal') and self.in_ is not None: - return False - elif self.if_ is not None: - if_ = self.if_ - while True: - for k, v in sorted(self.defines.items(), - key=lambda x: len(x[0]), reverse=True): - if k in if_: - if_ = if_.replace(k, '(%s)' % v) - break - else: - break - if_ = ( - re.sub('(\&\&|\?)', ' and ', - re.sub('(\|\||:)', ' or ', - re.sub('!(?!=)', ' not ', if_)))) - return eval(if_) - else: - return True - - def test(self, exec=[], persist=False, cycles=None, - gdb=False, failure=None, disk=None, **args): - # build command - cmd = exec + ['./%s.test' % self.suite.path, - repr(self.caseno), repr(self.permno)] - - # persist disk or keep in RAM for speed? - if persist: - if not disk: - disk = self.suite.path + '.disk' - if persist != 'noerase': - try: - with open(disk, 'w') as f: - f.truncate(0) - if args.get('verbose'): - print('truncate --size=0', disk) - except FileNotFoundError: - pass - - cmd.append(disk) - - # simulate power-loss after n cycles? - if cycles: - cmd.append(str(cycles)) - - # failed? drop into debugger? - if gdb and failure: - ncmd = ['gdb'] - if gdb == 'assert': - ncmd.extend(['-ex', 'r']) - if failure.assert_: - ncmd.extend(['-ex', 'up 2']) - elif gdb == 'main': - ncmd.extend([ - '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), - '-ex', 'r']) - ncmd.extend(['--args'] + cmd) - - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in ncmd)) - signal.signal(signal.SIGINT, signal.SIG_IGN) - sys.exit(sp.call(ncmd)) - - # run test case! - mpty, spty = pty.openpty() - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, stdout=spty, stderr=spty) - os.close(spty) - mpty = os.fdopen(mpty, 'r', 1) - stdout = [] - assert_ = None - try: - while True: - try: - line = mpty.readline() - except OSError as e: - if e.errno == errno.EIO: - break - raise - if not line: - break; - stdout.append(line) - if args.get('verbose'): - sys.stdout.write(line) - # intercept asserts - m = re.match( - '^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$' - .format('(?:\033\[[\d;]*.| )*', 'assert'), - line) - if m and assert_ is None: - try: - with open(m.group(1)) as f: - lineno = int(m.group(2)) - line = (next(it.islice(f, lineno-1, None)) - .strip('\n')) - assert_ = { - 'path': m.group(1), - 'line': line, - 'lineno': lineno, - 'message': m.group(3)} - except: - pass - except KeyboardInterrupt: - raise TestFailure(self, 1, stdout, None) - proc.wait() - - # did we pass? - if proc.returncode != 0: - raise TestFailure(self, proc.returncode, stdout, assert_) - else: - return PASS +def testpath(path): + path, *_ = path.split('#', 1) + return path -class ValgrindTestCase(TestCase): - def __init__(self, config, **args): - self.leaky = config.get('leaky', False) - super().__init__(config, **args) +def testsuite(path): + suite = testpath(path) + suite = os.path.basename(suite) + if suite.endswith('.toml'): + suite = suite[:-len('.toml')] + return suite - def shouldtest(self, **args): - return not self.leaky and super().shouldtest(**args) +def testcase(path): + _, case, *_ = path.split('#', 2) + return '%s#%s' % (testsuite(path), case) - def test(self, exec=[], **args): - verbose = args.get('verbose') - uninit = (self.defines.get('LFS_ERASE_VALUE', None) == -1) - exec = [ - 'valgrind', - '--leak-check=full', - ] + (['--undef-value-errors=no'] if uninit else []) + [ - ] + (['--track-origins=yes'] if not uninit else []) + [ - '--error-exitcode=4', - '--error-limit=no', - ] + (['--num-callers=1'] if not verbose else []) + [ - '-q'] + exec - return super().test(exec=exec, **args) - -class ReentrantTestCase(TestCase): - def __init__(self, config, **args): - self.reentrant = config.get('reentrant', False) - super().__init__(config, **args) - - def shouldtest(self, **args): - return self.reentrant and super().shouldtest(**args) - - def test(self, persist=False, gdb=False, failure=None, **args): - for cycles in it.count(1): - # clear disk first? - if cycles == 1 and persist != 'noerase': - persist = 'erase' - else: - persist = 'noerase' +# TODO move this out in other files +def openio(path, mode='r'): + if path == '-': + if 'r' in mode: + return os.fdopen(os.dup(sys.stdin.fileno()), 'r') + else: + return os.fdopen(os.dup(sys.stdout.fileno()), 'w') + else: + return open(path, mode) - # exact cycle we should drop into debugger? - if gdb and failure and failure.cycleno == cycles: - return super().test(gdb=gdb, persist=persist, cycles=cycles, - failure=failure, **args) +class TestCase: + # create a TestCase object from a config + def __init__(self, config, args={}): + self.name = config.pop('name') + self.path = config.pop('path') + self.suite = config.pop('suite') + self.lineno = config.pop('lineno', None) + self.if_ = config.pop('if', None) + if isinstance(self.if_, bool): + self.if_ = 'true' if self.if_ else 'false' + self.code = config.pop('code') + self.code_lineno = config.pop('code_lineno', None) + self.in_ = config.pop('in', + config.pop('suite_in', None)) + + self.normal = config.pop('normal', + config.pop('suite_normal', True)) + self.reentrant = config.pop('reentrant', + config.pop('suite_reentrant', False)) + + # figure out defines and build possible permutations + self.defines = set() + self.permutations = [] + + suite_defines = config.pop('suite_defines', {}) + if not isinstance(suite_defines, list): + suite_defines = [suite_defines] + defines = config.pop('defines', {}) + if not isinstance(defines, list): + defines = [defines] + + # build possible permutations + for suite_defines_ in suite_defines: + self.defines |= suite_defines_.keys() + for defines_ in defines: + self.defines |= defines_.keys() + self.permutations.extend(map(dict, it.product(*( + [(k, v) for v in (vs if isinstance(vs, list) else [vs])] + for k, vs in sorted( + (suite_defines_ | defines_).items()))))) + + for k in config.keys(): + print('\x1b[01;33mwarning:\x1b[m in %s, found unused key %r' + % (self.id(), k), + file=sys.stderr) + + def id(self): + return '%s#%s' % (self.suite, self.name) - # run tests, but kill the program after prog/erase has - # been hit n cycles. We exit with a special return code if the - # program has not finished, since this isn't a test failure. - try: - return super().test(persist=persist, cycles=cycles, **args) - except TestFailure as nfailure: - if nfailure.returncode == 33: - continue - else: - nfailure.cycleno = cycles - raise class TestSuite: - def __init__(self, path, classes=[TestCase], defines={}, - filter=None, **args): - self.name = os.path.basename(path) - if self.name.endswith('.toml'): - self.name = self.name[:-len('.toml')] - if args.get('build_dir'): - self.toml = path - self.path = args['build_dir'] + '/' + path - else: - self.toml = path - self.path = path - self.classes = classes - self.defines = defines.copy() - self.filter = filter + # create a TestSuite object from a toml file + def __init__(self, path, args={}): + self.name = testsuite(path) + self.path = testpath(path) - with open(self.toml) as f: + # load toml file and parse test cases + with open(self.path) as f: # load tests config = toml.load(f) # find line numbers f.seek(0) - linenos = [] + case_linenos = [] code_linenos = [] for i, line in enumerate(f): - if re.match(r'\[\[\s*case\s*\]\]', line): - linenos.append(i+1) - if re.match(r'code\s*=\s*(\'\'\'|""")', line): + match = re.match( + '(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])' + '|' '(?P<code>code\s*=)', + line) + if match and match.group('case'): + case_linenos.append((i+1, match.group('name'))) + elif match and match.group('code'): code_linenos.append(i+2) - code_linenos.reverse() - - # grab global config - for k, v in config.get('define', {}).items(): - if k not in self.defines: - self.defines[k] = v - self.code = config.get('code', None) - if self.code is not None: - self.code_lineno = code_linenos.pop() - - # create initial test cases - self.cases = [] - for i, (case, lineno) in enumerate(zip(config['case'], linenos)): - # code lineno? - if 'code' in case: - case['code_lineno'] = code_linenos.pop() - # merge conditions if necessary - if 'if' in config and 'if' in case: - case['if'] = '(%s) && (%s)' % (config['if'], case['if']) - elif 'if' in config: - case['if'] = config['if'] - # initialize test case - self.cases.append(TestCase(case, filter=filter, - suite=self, caseno=i+1, lineno=lineno, **args)) - - def __str__(self): + # sort in case toml parsing did not retain order + case_linenos.sort() + + cases = config.pop('cases') + for (lineno, name), (nlineno, _) in it.zip_longest( + case_linenos, case_linenos[1:], + fillvalue=(float('inf'), None)): + code_lineno = min( + (l for l in code_linenos if l >= lineno and l < nlineno), + default=None) + cases[name]['lineno'] = lineno + cases[name]['code_lineno'] = code_lineno + + self.if_ = config.pop('if', None) + if isinstance(self.if_, bool): + self.if_ = 'true' if self.if_ else 'false' + + self.code = config.pop('code', None) + self.code_lineno = min( + (l for l in code_linenos + if not case_linenos or l < case_linenos[0][0]), + default=None) + + # a couple of these we just forward to all cases + defines = config.pop('defines', {}) + in_ = config.pop('in', None) + normal = config.pop('normal', True) + reentrant = config.pop('reentrant', False) + + self.cases = [] + for name, case in sorted(cases.items(), + key=lambda c: c[1].get('lineno')): + self.cases.append(TestCase(config={ + 'name': name, + 'path': path + (':%d' % case['lineno'] + if 'lineno' in case else ''), + 'suite': self.name, + 'suite_defines': defines, + 'suite_in': in_, + 'suite_normal': normal, + 'suite_reentrant': reentrant, + **case})) + + # combine per-case defines + self.defines = set.union(*( + set(case.defines) for case in self.cases)) + + # combine other per-case things + self.normal = any(case.normal for case in self.cases) + self.reentrant = any(case.reentrant for case in self.cases) + + for k in config.keys(): + print('\x1b[01;33mwarning:\x1b[m in %s, found unused key %r' + % (self.id(), k), + file=sys.stderr) + + def id(self): return self.name - def __lt__(self, other): - return self.name < other.name - - def permute(self, **args): - for case in self.cases: - # lets find all parameterized definitions, in one of [args.D, - # suite.defines, case.defines, DEFINES]. Note that each of these - # can be either a dict of defines, or a list of dicts, expressing - # an initial set of permutations. - pending = [{}] - for inits in [self.defines, case.defines, DEFINES]: - if not isinstance(inits, list): - inits = [inits] - - npending = [] - for init, pinit in it.product(inits, pending): - ninit = pinit.copy() - for k, v in init.items(): - if k not in ninit: - try: - ninit[k] = eval(v) - except: - ninit[k] = v - npending.append(ninit) - - pending = npending - - # expand permutations - pending = list(reversed(pending)) - expanded = [] - while pending: - perm = pending.pop() - for k, v in sorted(perm.items()): - if not isinstance(v, str) and isinstance(v, abc.Iterable): - for nv in reversed(v): - nperm = perm.copy() - nperm[k] = nv - pending.append(nperm) - break - else: - expanded.append(perm) - - # generate permutations - case.perms = [] - for i, (class_, defines) in enumerate( - it.product(self.classes, expanded)): - case.perms.append(case.permute( - class_, defines, permno=i+1, **args)) - - # also track non-unique defines - case.defines = {} - for k, v in case.perms[0].defines.items(): - if all(perm.defines[k] == v for perm in case.perms): - case.defines[k] = v - - # track all perms and non-unique defines - self.perms = [] - for case in self.cases: - self.perms.extend(case.perms) - - self.defines = {} - for k, v in self.perms[0].defines.items(): - if all(perm.defines.get(k, None) == v for perm in self.perms): - self.defines[k] = v - - return self.perms - - def build(self, **args): - # build test files - tf = open(self.path + '.test.tc', 'w') - tf.write(GLOBALS) - if self.code is not None: - tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) - tf.write(self.code) - - tfs = {None: tf} - for case in self.cases: - if case.in_ not in tfs: - tfs[case.in_] = open(self.path+'.'+ - re.sub('(\.c)?$', '.tc', case.in_.replace('/', '.')), 'w') - tfs[case.in_].write('#line 1 "%s"\n' % case.in_) - with open(case.in_) as f: - for line in f: - tfs[case.in_].write(line) - tfs[case.in_].write('\n') - tfs[case.in_].write(GLOBALS) - - tfs[case.in_].write('\n') - case.build(tfs[case.in_], **args) - - tf.write('\n') - tf.write('const char *lfs_testbd_path;\n') - tf.write('uint32_t lfs_testbd_cycles;\n') - tf.write('int main(int argc, char **argv) {\n') - tf.write(4*' '+'int case_ = (argc > 1) ? atoi(argv[1]) : 0;\n') - tf.write(4*' '+'int perm = (argc > 2) ? atoi(argv[2]) : 0;\n') - tf.write(4*' '+'lfs_testbd_path = (argc > 3) ? argv[3] : NULL;\n') - tf.write(4*' '+'lfs_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 0;\n') - for perm in self.perms: - # test declaration - tf.write(4*' '+'extern void test_case%d(%s);\n' % ( - perm.caseno, ', '.join( - 'intmax_t %s' % k for k in sorted(perm.defines) - if k not in perm.case.defines))) - # test call - tf.write(4*' '+ - 'if (argc < 3 || (case_ == %d && perm == %d)) {' - ' test_case%d(%s); ' - '}\n' % (perm.caseno, perm.permno, perm.caseno, ', '.join( - str(v) for k, v in sorted(perm.defines.items()) - if k not in perm.case.defines))) - tf.write('}\n') - - for tf in tfs.values(): - tf.close() - - # write makefiles - with open(self.path + '.mk', 'w') as mk: - mk.write(RULES.replace(4*' ', '\t') % dict(path=self.path)) - mk.write('\n') - - # add coverage hooks? - if args.get('coverage'): - mk.write(COVERAGE_RULES.replace(4*' ', '\t') % dict( - path=self.path)) - mk.write('\n') - - # add truly global defines globally - for k, v in sorted(self.defines.items()): - mk.write('%s.test: override CFLAGS += -D%s=%r\n' - % (self.path, k, v)) - - for path in tfs: - if path is None: - mk.write('%s: %s | %s\n' % ( - self.path+'.test.c', - self.toml, - self.path+'.test.tc')) - else: - mk.write('%s: %s %s | %s\n' % ( - self.path+'.'+path.replace('/', '.'), - self.toml, - path, - self.path+'.'+re.sub('(\.c)?$', '.tc', - path.replace('/', '.')))) - mk.write('\t./scripts/explode_asserts.py $| -o $@\n') - - self.makefile = self.path + '.mk' - self.target = self.path + '.test' - return self.makefile, self.target - - def test(self, **args): - # run test suite! - if not args.get('verbose', True): - sys.stdout.write(self.name + ' ') - sys.stdout.flush() - for perm in self.perms: - if not perm.shouldtest(**args): - continue - try: - result = perm.test(**args) - except TestFailure as failure: - perm.result = failure - if not args.get('verbose', True): - sys.stdout.write(FAIL) - sys.stdout.flush() - if not args.get('keep_going'): - if not args.get('verbose', True): - sys.stdout.write('\n') - raise - else: - perm.result = PASS - if not args.get('verbose', True): - sys.stdout.write(PASS) - sys.stdout.flush() - if not args.get('verbose', True): - sys.stdout.write('\n') +def compile(**args): + # find .toml files + paths = [] + for path in args.get('test_ids', TEST_PATHS): + if os.path.isdir(path): + path = path + '/*.toml' -def main(**args): - # figure out explicit defines - defines = {} - for define in args['D']: - k, v, *_ = define.split('=', 2) + [''] - defines[k] = v - - # and what class of TestCase to run - classes = [] - if args.get('normal'): - classes.append(TestCase) - if args.get('reentrant'): - classes.append(ReentrantTestCase) + for path in glob.glob(path): + paths.append(path) + + if not paths: + print('no test suites found in %r?' % args['test_ids']) + sys.exit(-1) + + if not args.get('source'): + if len(paths) > 1: + print('more than one test suite for compilation? (%r)' + % args['test_ids']) + sys.exit(-1) + + # load our suite + suite = TestSuite(paths[0]) + else: + # load all suites + suites = [TestSuite(path) for path in paths] + suites.sort(key=lambda s: s.name) + + # write generated test source + if 'output' in args: + with openio(args['output'], 'w') as f: + _write = f.write + def write(s): + f.lineno += s.count('\n') + _write(s) + def writeln(s=''): + f.lineno += s.count('\n') + 1 + _write(s) + _write('\n') + f.lineno = 1 + f.write = write + f.writeln = writeln + + # redirect littlefs tracing + f.writeln('#define LFS_TRACE_(fmt, ...) do { \\') + f.writeln(8*' '+'extern FILE *test_trace; \\') + f.writeln(8*' '+'if (test_trace) { \\') + f.writeln(12*' '+'fprintf(test_trace, ' + '"%s:%d:trace: " fmt "%s\\n", \\') + f.writeln(20*' '+'__FILE__, __LINE__, __VA_ARGS__); \\') + f.writeln(8*' '+'} \\') + f.writeln(4*' '+'} while (0)') + f.writeln('#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")') + f.writeln('#define LFS_TESTBD_TRACE(...) ' + 'LFS_TRACE_(__VA_ARGS__, "")') + f.writeln() + + # write out generated functions, this can end up in different + # files depending on the "in" attribute + # + # note it's up to the specific generated file to declare + # the test defines + def write_case_functions(f, suite, case): + # create case define functions + if case.defines: + # deduplicate defines by value to try to reduce the + # number of functions we generate + define_cbs = {} + for i, defines in enumerate(case.permutations): + for k, v in sorted(defines.items()): + if v not in define_cbs: + name = ('__test__%s__%s__%s__%d' + % (suite.name, case.name, k, i)) + define_cbs[v] = name + f.writeln('intmax_t %s(void) {' % name) + f.writeln(4*' '+'return %s;' % v) + f.writeln('}') + f.writeln() + f.writeln('intmax_t (*const *const ' + '__test__%s__%s__defines[])(void) = {' + % (suite.name, case.name)) + for defines in case.permutations: + f.writeln(4*' '+'(intmax_t (*const[])(void)){') + for define in sorted(suite.defines): + f.writeln(8*' '+'%s,' % ( + define_cbs[defines[define]] + if define in defines + else 'NULL')) + f.writeln(4*' '+'},') + f.writeln('};') + f.writeln() + + # create case filter function + if suite.if_ is not None or case.if_ is not None: + f.writeln('bool __test__%s__%s__filter(void) {' + % (suite.name, case.name)) + f.writeln(4*' '+'return %s;' + % ' && '.join('(%s)' % if_ + for if_ in [suite.if_, case.if_] + if if_ is not None)) + f.writeln('}') + f.writeln() + + # create case run function + f.writeln('void __test__%s__%s__run(' + '__attribute__((unused)) struct lfs_config *cfg) {' + % (suite.name, case.name)) + if CASE_PROLOGUE.strip(): + f.writeln(4*' '+'%s' + % CASE_PROLOGUE.strip().replace('\n', '\n'+4*' ')) + f.writeln() + f.writeln(4*' '+'// test case %s' % case.id()) + if case.code_lineno is not None: + f.writeln(4*' '+'#line %d "%s"' + % (case.code_lineno, suite.path)) + f.write(case.code) + if case.code_lineno is not None: + f.writeln(4*' '+'#line %d "%s"' + % (f.lineno+1, args['output'])) + if CASE_EPILOGUE.strip(): + f.writeln() + f.writeln(4*' '+'%s' + % CASE_EPILOGUE.strip().replace('\n', '\n'+4*' ')) + f.writeln('}') + f.writeln() + + if not args.get('source'): + # write test suite prologue + f.writeln('%s' % SUITE_PROLOGUE.strip()) + f.writeln() + if suite.code is not None: + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (suite.code_lineno, suite.path)) + f.write(suite.code) + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (f.lineno+1, args['output'])) + f.writeln() + + if suite.defines: + for i, define in enumerate(sorted(suite.defines)): + f.writeln('#ifndef %s' % define) + f.writeln('#define %-24s test_define(%d)' + % (define, i)) + f.writeln('#endif') + f.writeln() + + for case in suite.cases: + # create case functions + if case.in_ is None: + write_case_functions(f, suite, case) + else: + if case.defines: + f.writeln('extern intmax_t (*const *const ' + '__test__%s__%s__defines[])(void);' + % (suite.name, case.name)) + if suite.if_ is not None or case.if_ is not None: + f.writeln('extern bool __test__%s__%s__filter(' + 'void);' + % (suite.name, case.name)) + f.writeln('extern void __test__%s__%s__run(' + 'struct lfs_config *cfg);' + % (suite.name, case.name)) + f.writeln() + + # create case struct + f.writeln('const struct test_case __test__%s__%s__case = {' + % (suite.name, case.name)) + f.writeln(4*' '+'.id = "%s",' % case.id()) + f.writeln(4*' '+'.name = "%s",' % case.name) + f.writeln(4*' '+'.path = "%s",' % case.path) + f.writeln(4*' '+'.types = %s,' + % ' | '.join(filter(None, [ + 'TEST_NORMAL' if case.normal else None, + 'TEST_REENTRANT' if case.reentrant else None]))) + f.writeln(4*' '+'.permutations = %d,' + % len(case.permutations)) + if case.defines: + f.writeln(4*' '+'.defines = __test__%s__%s__defines,' + % (suite.name, case.name)) + if suite.if_ is not None or case.if_ is not None: + f.writeln(4*' '+'.filter = __test__%s__%s__filter,' + % (suite.name, case.name)) + f.writeln(4*' '+'.run = __test__%s__%s__run,' + % (suite.name, case.name)) + f.writeln('};') + f.writeln() + + # create suite define names + if suite.defines: + f.writeln('const char *const __test__%s__define_names[] = {' + % suite.name) + for k in sorted(suite.defines): + f.writeln(4*' '+'"%s",' % k) + f.writeln('};') + f.writeln() + + # create suite struct + f.writeln('const struct test_suite __test__%s__suite = {' + % suite.name) + f.writeln(4*' '+'.id = "%s",' % suite.id()) + f.writeln(4*' '+'.name = "%s",' % suite.name) + f.writeln(4*' '+'.path = "%s",' % suite.path) + f.writeln(4*' '+'.types = %s,' + % ' | '.join(filter(None, [ + 'TEST_NORMAL' if suite.normal else None, + 'TEST_REENTRANT' if suite.reentrant else None]))) + if suite.defines: + f.writeln(4*' '+'.define_names = __test__%s__define_names,' + % suite.name) + f.writeln(4*' '+'.define_count = %d,' % len(suite.defines)) + f.writeln(4*' '+'.cases = (const struct test_case *const []){') + for case in suite.cases: + f.writeln(8*' '+'&__test__%s__%s__case,' + % (suite.name, case.name)) + f.writeln(4*' '+'},') + f.writeln(4*' '+'.case_count = %d,' % len(suite.cases)) + f.writeln('};') + f.writeln() + + else: + # copy source + f.writeln('#line 1 "%s"' % args['source']) + with open(args['source']) as sf: + shutil.copyfileobj(sf, f) + f.writeln() + + f.write(SUITE_PROLOGUE) + f.writeln() + + # write any internal tests + for suite in suites: + for case in suite.cases: + if (case.in_ is not None + and os.path.normpath(case.in_) + == os.path.normpath(args['source'])): + # write defines, but note we need to undef any + # new defines since we're in someone else's file + if suite.defines: + for i, define in enumerate( + sorted(suite.defines)): + f.writeln('#ifndef %s' % define) + f.writeln('#define %-24s test_define(%d)' + % (define, i)) + f.writeln('#define __TEST__%s__NEEDS_UNDEF' + % define) + f.writeln('#endif') + f.writeln() + + write_case_functions(f, suite, case) + + if suite.defines: + for define in sorted(suite.defines): + f.writeln('#ifdef __TEST__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef __TEST__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef %s' % define) + f.writeln('#endif') + f.writeln() + + # add suite info to test_runner.c + if args['source'] == 'runners/test_runner.c': + f.writeln() + for suite in suites: + f.writeln('extern const struct test_suite ' + '__test__%s__suite;' % suite.name) + f.writeln('const struct test_suite *test_suites[] = {') + for suite in suites: + f.writeln(4*' '+'&__test__%s__suite,' % suite.name) + f.writeln('};') + f.writeln('const size_t test_suite_count = %d;' + % len(suites)) + +def runner(**args): + cmd = args['runner'].copy() + cmd.extend(args.get('test_ids')) + + # run under some external command? + cmd[:0] = args.get('exec', []) + + # run under valgrind? if args.get('valgrind'): - classes.append(ValgrindTestCase) - if not classes: - classes = [TestCase] - - suites = [] - for testpath in args['test_paths']: - # optionally specified test case/perm - testpath, *filter = testpath.split('#') - filter = [int(f) for f in filter] - - # figure out the suite's toml file - if os.path.isdir(testpath): - testpath = testpath + '/*.toml' - elif os.path.isfile(testpath): - testpath = testpath - elif testpath.endswith('.toml'): - testpath = TEST_PATHS + '/' + testpath - else: - testpath = TEST_PATHS + '/' + testpath + '.toml' - - # find tests - for path in glob.glob(testpath): - suites.append(TestSuite(path, classes, defines, filter, **args)) - - # sort for reproducibility - suites = sorted(suites) - - # generate permutations - for suite in suites: - suite.permute(**args) - - # build tests in parallel - print('====== building ======') - makefiles = [] - targets = [] - for suite in suites: - makefile, target = suite.build(**args) - makefiles.append(makefile) - targets.append(target) - - cmd = (['make', '-f', 'Makefile'] + - list(it.chain.from_iterable(['-f', m] for m in makefiles)) + - [target for target in targets]) - mpty, spty = pty.openpty() + cmd[:0] = filter(None, [ + 'valgrind', + '--leak-check=full', + '--track-origins=yes', + '--error-exitcode=4', + '-q']) + + # filter tests? + if args.get('normal'): cmd.append('-n') + if args.get('reentrant'): cmd.append('-r') + if args.get('geometry'): + cmd.append('-G%s' % args.get('geometry')) + + # defines? + if args.get('define'): + for define in args.get('define'): + cmd.append('-D%s' % define) + + return cmd + +def list_(**args): + cmd = runner(**args) + if args.get('summary'): cmd.append('--summary') + if args.get('list_suites'): cmd.append('--list-suites') + if args.get('list_cases'): cmd.append('--list-cases') + if args.get('list_paths'): cmd.append('--list-paths') + if args.get('list_defines'): cmd.append('--list-defines') + if args.get('list_geometries'): cmd.append('--list-geometries') + if args.get('verbose'): print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, stdout=spty, stderr=spty) - os.close(spty) - mpty = os.fdopen(mpty, 'r', 1) - stdout = [] - while True: - try: - line = mpty.readline() - except OSError as e: - if e.errno == errno.EIO: - break - raise - if not line: - break; - stdout.append(line) - if args.get('verbose'): - sys.stdout.write(line) - # intercept warnings - m = re.match( - '^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$' - .format('(?:\033\[[\d;]*.| )*', 'warning'), - line) - if m and not args.get('verbose'): - try: - with open(m.group(1)) as f: - lineno = int(m.group(2)) - line = next(it.islice(f, lineno-1, None)).strip('\n') - sys.stdout.write( - "\033[01m{path}:{lineno}:\033[01;35mwarning:\033[m " - "{message}\n{line}\n\n".format( - path=m.group(1), line=line, lineno=lineno, - message=m.group(3))) - except: - pass + sys.exit(sp.call(cmd)) + + +def find_cases(runner_, **args): + # query from runner + cmd = runner_ + ['--list-cases'] + if args.get('verbose'): + print(' '.join(shlex.quote(c) for c in cmd)) + proc = sp.Popen(cmd, + stdout=sp.PIPE, + stderr=sp.PIPE if not args.get('verbose') else None, + universal_newlines=True, + errors='replace') + expected_suite_perms = co.defaultdict(lambda: 0) + expected_case_perms = co.defaultdict(lambda: 0) + expected_perms = 0 + total_perms = 0 + pattern = re.compile( + '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' + '[^\s]+\s+(?P<filtered>\d+)/(?P<perms>\d+)') + # skip the first line + for line in it.islice(proc.stdout, 1, None): + m = pattern.match(line) + if m: + filtered = int(m.group('filtered')) + expected_suite_perms[m.group('suite')] += filtered + expected_case_perms[m.group('id')] += filtered + expected_perms += filtered + total_perms += int(m.group('perms')) proc.wait() if proc.returncode != 0: if not args.get('verbose'): - for line in stdout: + for line in proc.stderr: sys.stdout.write(line) sys.exit(-1) - print('built %d test suites, %d test cases, %d permutations' % ( - len(suites), - sum(len(suite.cases) for suite in suites), - sum(len(suite.perms) for suite in suites))) + return ( + expected_suite_perms, + expected_case_perms, + expected_perms, + total_perms) - total = 0 - for suite in suites: - for perm in suite.perms: - total += perm.shouldtest(**args) - if total != sum(len(suite.perms) for suite in suites): - print('filtered down to %d permutations' % total) +def find_paths(runner_, **args): + # query from runner + cmd = runner_ + ['--list-paths'] + if args.get('verbose'): + print(' '.join(shlex.quote(c) for c in cmd)) + proc = sp.Popen(cmd, + stdout=sp.PIPE, + stderr=sp.PIPE if not args.get('verbose') else None, + universal_newlines=True, + errors='replace') + paths = co.OrderedDict() + pattern = re.compile( + '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' + '(?P<path>[^:]+):(?P<lineno>\d+)') + for line in proc.stdout: + m = pattern.match(line) + if m: + paths[m.group('id')] = (m.group('path'), int(m.group('lineno'))) + proc.wait() + if proc.returncode != 0: + if not args.get('verbose'): + for line in proc.stderr: + sys.stdout.write(line) + sys.exit(-1) - # only requested to build? - if args.get('build'): - return 0 + return paths - print('====== testing ======') - try: - for suite in suites: - suite.test(**args) - except TestFailure: - pass +def find_defines(runner_, **args): + # query from runner + cmd = runner_ + ['--list-defines'] + if args.get('verbose'): + print(' '.join(shlex.quote(c) for c in cmd)) + proc = sp.Popen(cmd, + stdout=sp.PIPE, + stderr=sp.PIPE if not args.get('verbose') else None, + universal_newlines=True, + errors='replace') + defines = co.OrderedDict() + pattern = re.compile( + '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' + '(?P<defines>(?:\w+=\w+\s*)+)') + for line in proc.stdout: + m = pattern.match(line) + if m: + defines[m.group('id')] = {k: v + for k, v in re.findall('(\w+)=(\w+)', m.group('defines'))} + proc.wait() + if proc.returncode != 0: + if not args.get('verbose'): + for line in proc.stderr: + sys.stdout.write(line) + sys.exit(-1) - print('====== results ======') - passed = 0 - failed = 0 - for suite in suites: - for perm in suite.perms: - if perm.result == PASS: - passed += 1 - elif isinstance(perm.result, TestFailure): - sys.stdout.write( - "\033[01m{path}:{lineno}:\033[01;31mfailure:\033[m " - "{perm} failed\n".format( - perm=perm, path=perm.suite.path, lineno=perm.lineno, - returncode=perm.result.returncode or 0)) - if perm.result.stdout: - if perm.result.assert_: - stdout = perm.result.stdout[:-1] - else: - stdout = perm.result.stdout - for line in stdout[-5:]: - sys.stdout.write(line) - if perm.result.assert_: - sys.stdout.write( - "\033[01m{path}:{lineno}:\033[01;31massert:\033[m " - "{message}\n{line}\n".format( - **perm.result.assert_)) - sys.stdout.write('\n') - failed += 1 - - if args.get('coverage'): - # collect coverage info - # why -j1? lcov doesn't work in parallel because of gcov limitations - cmd = (['make', '-j1', '-f', 'Makefile'] + - list(it.chain.from_iterable(['-f', m] for m in makefiles)) + - (['COVERAGETARGET=%s' % args['coverage']] - if isinstance(args['coverage'], str) else []) + - [suite.path + '.info' for suite in suites - if any(perm.result == PASS for perm in suite.perms)]) + return defines + + +class TestFailure(Exception): + def __init__(self, id, returncode, output, assert_=None): + self.id = id + self.returncode = returncode + self.output = output + self.assert_ = assert_ + +def run_stage(name, runner_, **args): + # get expected suite/case/perm counts + expected_suite_perms, expected_case_perms, expected_perms, total_perms = ( + find_cases(runner_, **args)) + + passed_suite_perms = co.defaultdict(lambda: 0) + passed_case_perms = co.defaultdict(lambda: 0) + passed_perms = 0 + failures = [] + killed = False + + pattern = re.compile('^(?:' + '(?P<op>running|finished|skipped) ' + '(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)' + '|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):' + ' *(?P<message>.*)' ')$') + locals = th.local() + children = set() + + def run_runner(runner_): + nonlocal passed_suite_perms + nonlocal passed_case_perms + nonlocal passed_perms + nonlocal locals + + # run the tests! + cmd = runner_.copy() + if args.get('disk'): + cmd.append('--disk=%s' % args['disk']) + if args.get('trace'): + cmd.append('--trace=%s' % args['trace']) if args.get('verbose'): print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - stdout=sp.PIPE if not args.get('verbose') else None, - stderr=sp.STDOUT if not args.get('verbose') else None, - universal_newlines=True) - stdout = [] - for line in proc.stdout: - stdout.append(line) + mpty, spty = pty.openpty() + proc = sp.Popen(cmd, stdout=spty, stderr=spty) + os.close(spty) + children.add(proc) + mpty = os.fdopen(mpty, 'r', 1) + if args.get('output'): + output = openio(args['output'], 'w') + + last_id = None + last_output = [] + last_assert = None + try: + while True: + # parse a line for state changes + try: + line = mpty.readline() + except OSError as e: + if e.errno == errno.EIO: + break + raise + if not line: + break + last_output.append(line) + if args.get('output'): + output.write(line) + elif args.get('verbose'): + sys.stdout.write(line) + + m = pattern.match(line) + if m: + op = m.group('op') or m.group('op_') + if op == 'running': + locals.seen_perms += 1 + last_id = m.group('id') + last_output = [] + last_assert = None + elif op == 'finished': + passed_suite_perms[m.group('suite')] += 1 + passed_case_perms[m.group('case')] += 1 + passed_perms += 1 + elif op == 'skipped': + locals.seen_perms += 1 + elif op == 'assert': + last_assert = ( + m.group('path'), + int(m.group('lineno')), + m.group('message')) + # go ahead and kill the process, aborting takes a while + if args.get('keep_going'): + proc.kill() + except KeyboardInterrupt: + raise TestFailure(last_id, 1, last_output) + finally: + children.remove(proc) + mpty.close() + if args.get('output'): + output.close() + proc.wait() if proc.returncode != 0: + raise TestFailure( + last_id, + proc.returncode, + last_output, + last_assert) + + def run_job(runner, start=None, step=None): + nonlocal failures + nonlocal locals + + start = start or 0 + step = step or 1 + while start < total_perms: + runner_ = runner.copy() + if start is not None: + runner_.append('--start=%d' % start) + if step is not None: + runner_.append('--step=%d' % step) + if args.get('isolate') or args.get('valgrind'): + runner_.append('--stop=%d' % (start+step)) + + try: + # run the tests + locals.seen_perms = 0 + run_runner(runner_) + assert locals.seen_perms > 0 + start += locals.seen_perms*step + + except TestFailure as failure: + # race condition for multiple failures? + if failures and not args.get('keep_going'): + break + + failures.append(failure) + + if args.get('keep_going') and not killed: + # resume after failed test + assert locals.seen_perms > 0 + start += locals.seen_perms*step + continue + else: + # stop other tests + for child in children.copy(): + child.kill() + + + # parallel jobs? + runners = [] + if 'jobs' in args: + for job in range(args['jobs']): + runners.append(th.Thread( + target=run_job, args=(runner_, job, args['jobs']))) + else: + runners.append(th.Thread( + target=run_job, args=(runner_, None, None))) + + for r in runners: + r.start() + + needs_newline = False + try: + while any(r.is_alive() for r in runners): + time.sleep(0.01) + if not args.get('verbose'): - for line in stdout: - sys.stdout.write(line) - sys.exit(-1) + sys.stdout.write('\r\x1b[K' + 'running \x1b[%dm%s:\x1b[m %s ' + % (32 if not failures else 31, + name, + ', '.join(filter(None, [ + '%d/%d suites' % ( + sum(passed_suite_perms[k] == v + for k, v in expected_suite_perms.items()), + len(expected_suite_perms)) + if (not args.get('by_suites') + and not args.get('by_cases')) else None, + '%d/%d cases' % ( + sum(passed_case_perms[k] == v + for k, v in expected_case_perms.items()), + len(expected_case_perms)) + if not args.get('by_cases') else None, + '%d/%d perms' % (passed_perms, expected_perms), + '\x1b[31m%d/%d failures\x1b[m' + % (len(failures), expected_perms) + if failures else None])))) + sys.stdout.flush() + needs_newline = True + except KeyboardInterrupt: + # this is handled by the runner threads, we just + # need to not abort here + killed = True + finally: + if needs_newline: + print() + + for r in runners: + r.join() + + return ( + expected_perms, + passed_perms, + failures, + killed) + + +def run(**args): + start = time.time() + + runner_ = runner(**args) + print('using runner: %s' + % ' '.join(shlex.quote(c) for c in runner_)) + expected_suite_perms, expected_case_perms, expected_perms, total_perms = ( + find_cases(runner_, **args)) + print('found %d suites, %d cases, %d/%d permutations' + % (len(expected_suite_perms), + len(expected_case_perms), + expected_perms, + total_perms)) + print() + + expected = 0 + passed = 0 + failures = [] + for type, by in it.product( + ['normal', 'reentrant'], + expected_case_perms.keys() if args.get('by_cases') + else expected_suite_perms.keys() if args.get('by_suites') + else [None]): + # rebuild runner for each stage to override test identifier if needed + stage_runner = runner(**args | { + 'test_ids': [by] if by is not None else args.get('test_ids', []), + 'normal': type == 'normal', + 'reentrant': type == 'reentrant'}) + + # spawn jobs for stage + expected_, passed_, failures_, killed = run_stage( + '%s %s' % (type, by or 'tests'), stage_runner, **args) + expected += expected_ + passed += passed_ + failures.extend(failures_) + if (failures and not args.get('keep_going')) or killed: + break + + # show summary + print() + print('\x1b[%dmdone:\x1b[m %d/%d passed, %d/%d failed, in %.2fs' + % (32 if not failures else 31, + passed, expected, len(failures), expected, + time.time()-start)) + print() + + # print each failure + if failures: + # get some extra info from runner + runner_paths = find_paths(runner_, **args) + runner_defines = find_defines(runner_, **args) + + for failure in failures: + # show summary of failure + path, lineno = runner_paths[testcase(failure.id)] + defines = runner_defines[failure.id] + + print('\x1b[01m%s:%d:\x1b[01;31mfailure:\x1b[m %s%s failed' + % (path, lineno, failure.id, + ' (%s)' % ', '.join( + '%s=%s' % (k, v) for k, v in defines.items()) + if defines else '')) + + if failure.output: + output = failure.output + if failure.assert_ is not None: + output = output[:-1] + for line in output[-5:]: + sys.stdout.write(line) + + if failure.assert_ is not None: + path, lineno, message = failure.assert_ + print('\x1b[01m%s:%d:\x1b[01;31massert:\x1b[m %s' + % (path, lineno, message)) + with open(path) as f: + line = next(it.islice(f, lineno-1, None)).strip('\n') + print(line) + print() + + # drop into gdb? + if failures and (args.get('gdb') + or args.get('gdb_case') + or args.get('gdb_main')): + failure = failures[0] + runner_ = runner(**args | {'test_ids': [failure.id]}) + + if args.get('gdb_main'): + cmd = ['gdb', + '-ex', 'break main', + '-ex', 'run', + '--args'] + runner_ + elif args.get('gdb_case'): + path, lineno = runner_paths[testcase(failure.id)] + cmd = ['gdb', + '-ex', 'break %s:%d' % (path, lineno), + '-ex', 'run', + '--args'] + runner_ + elif failure.assert_ is not None: + cmd = ['gdb', + '-ex', 'run', + '-ex', 'frame function raise', + '-ex', 'up 2', + '--args'] + runner_ + else: + cmd = ['gdb', + '-ex', 'run', + '--args'] + runner_ + + # exec gdb interactively + if args.get('verbose'): + print(' '.join(shlex.quote(c) for c in cmd)) + os.execvp(cmd[0], cmd) + + return 1 if failures else 0 + + +def main(**args): + if args.get('compile'): + compile(**args) + elif (args.get('summary') + or args.get('list_suites') + or args.get('list_cases') + or args.get('list_paths') + or args.get('list_defines') + or args.get('list_geometries') + or args.get('list_defaults')): + list_(**args) + else: + run(**args) - if args.get('gdb'): - failure = None - for suite in suites: - for perm in suite.perms: - if isinstance(perm.result, TestFailure): - failure = perm.result - if failure is not None: - print('======= gdb ======') - # drop into gdb - failure.case.test(failure=failure, **args) - sys.exit(0) - - print('tests passed %d/%d (%.1f%%)' % (passed, total, - 100*(passed/total if total else 1.0))) - print('tests failed %d/%d (%.1f%%)' % (failed, total, - 100*(failed/total if total else 1.0))) - return 1 if failed > 0 else 0 if __name__ == "__main__": import argparse + import sys parser = argparse.ArgumentParser( - description="Run parameterized tests in various configurations.") - parser.add_argument('test_paths', nargs='*', default=[TEST_PATHS], - help="Description of test(s) to run. By default, this is all tests \ - found in the \"{0}\" directory. Here, you can specify a different \ - directory of tests, a specific file, a suite by name, and even \ - specific test cases and permutations. For example \ - \"test_dirs#1\" or \"{0}/test_dirs.toml#1#1\".".format(TEST_PATHS)) - parser.add_argument('-D', action='append', default=[], - help="Overriding parameter definitions.") + description="Build and run tests.", + conflict_handler='resolve') + parser.add_argument('test_ids', nargs='*', + help="Description of testis to run. May be a directory, path, or \ + test identifier. Test identifiers are of the form \ + <suite_name>#<case_name>#<permutation>, but suffixes can be \ + dropped to run any matching tests. Defaults to %r." % TEST_PATHS) parser.add_argument('-v', '--verbose', action='store_true', - help="Output everything that is happening.") - parser.add_argument('-k', '--keep-going', action='store_true', - help="Run all tests instead of stopping on first error. Useful for CI.") - parser.add_argument('-p', '--persist', choices=['erase', 'noerase'], - nargs='?', const='erase', - help="Store disk image in a file.") - parser.add_argument('-b', '--build', action='store_true', - help="Only build the tests, do not execute.") - parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], - nargs='?', const='assert', + help="Output commands that run behind the scenes.") + # test flags + test_parser = parser.add_argument_group('test options') + test_parser.add_argument('-Y', '--summary', action='store_true', + help="Show quick summary.") + test_parser.add_argument('-l', '--list-suites', action='store_true', + help="List test suites.") + test_parser.add_argument('-L', '--list-cases', action='store_true', + help="List test cases.") + test_parser.add_argument('--list-paths', action='store_true', + help="List the path for each test case.") + test_parser.add_argument('--list-defines', action='store_true', + help="List the defines for each test permutation.") + test_parser.add_argument('--list-geometries', action='store_true', + help="List the disk geometries used for testing.") + test_parser.add_argument('--list-defaults', action='store_true', + help="List the default defines in this test-runner.") + test_parser.add_argument('-D', '--define', action='append', + help="Override a test define.") + test_parser.add_argument('-G', '--geometry', + help="Filter by geometry.") + test_parser.add_argument('-n', '--normal', action='store_true', + help="Filter for normal tests. Can be combined.") + test_parser.add_argument('-r', '--reentrant', action='store_true', + help="Filter for reentrant tests. Can be combined.") + test_parser.add_argument('-d', '--disk', + help="Use this file as the disk.") + test_parser.add_argument('-t', '--trace', + help="Redirect trace output to this file.") + test_parser.add_argument('-o', '--output', + help="Redirect stdout and stderr to this file.") + test_parser.add_argument('--runner', default=[RUNNER_PATH], + type=lambda x: x.split(), + help="Path to runner, defaults to %r" % RUNNER_PATH) + test_parser.add_argument('-j', '--jobs', nargs='?', type=int, + const=len(os.sched_getaffinity(0)), + help="Number of parallel runners to run.") + test_parser.add_argument('-k', '--keep-going', action='store_true', + help="Don't stop on first error.") + test_parser.add_argument('-i', '--isolate', action='store_true', + help="Run each test permutation in a separate process.") + test_parser.add_argument('-b', '--by-suites', action='store_true', + help="Step through tests by suite.") + test_parser.add_argument('-B', '--by-cases', action='store_true', + help="Step through tests by case.") + test_parser.add_argument('--gdb', action='store_true', help="Drop into gdb on test failure.") - parser.add_argument('--no-internal', action='store_true', - help="Don't run tests that require internal knowledge.") - parser.add_argument('-n', '--normal', action='store_true', - help="Run tests normally.") - parser.add_argument('-r', '--reentrant', action='store_true', - help="Run reentrant tests with simulated power-loss.") - parser.add_argument('--valgrind', action='store_true', - help="Run non-leaky tests under valgrind to check for memory leaks.") - parser.add_argument('--exec', default=[], type=lambda e: e.split(), - help="Run tests with another executable prefixed on the command line.") - parser.add_argument('--disk', - help="Specify a file to use for persistent/reentrant tests.") - parser.add_argument('--coverage', type=lambda x: x if x else True, - nargs='?', const='', - help="Collect coverage information during testing. This uses lcov/gcov \ - to accumulate coverage information into *.info files. May also \ - a path to a *.info file to accumulate coverage info into.") - parser.add_argument('--build-dir', - help="Build relative to the specified directory instead of the \ - current directory.") - - sys.exit(main(**vars(parser.parse_args()))) + test_parser.add_argument('--gdb-case', action='store_true', + help="Drop into gdb on test failure but stop at the beginning \ + of the failing test case.") + test_parser.add_argument('--gdb-main', action='store_true', + help="Drop into gdb on test failure but stop at the beginning \ + of main.") + test_parser.add_argument('--valgrind', action='store_true', + help="Run under Valgrind to find memory errors. Implicitly sets \ + --isolate.") + test_parser.add_argument('--exec', default=[], type=lambda e: e.split(), + help="Run under another executable.") + # compilation flags + comp_parser = parser.add_argument_group('compilation options') + comp_parser.add_argument('-c', '--compile', action='store_true', + help="Compile a test suite or source file.") + comp_parser.add_argument('-s', '--source', + help="Source file to compile, possibly injecting internal tests.") + comp_parser.add_argument('-o', '--output', + help="Output file.") + # TODO apply this to other scripts? + sys.exit(main(**{k: v + for k, v in vars(parser.parse_args()).items() + if v is not None})) diff --git a/scripts/test_.py b/scripts/test_.py deleted file mode 100755 index 02147ef..0000000 --- a/scripts/test_.py +++ /dev/null @@ -1,1027 +0,0 @@ -#!/usr/bin/env python3 -# -# Script to compile and runs tests. -# - -import collections as co -import errno -import glob -import itertools as it -import math as m -import os -import pty -import re -import shlex -import shutil -import signal -import subprocess as sp -import threading as th -import time -import toml - - -TEST_PATHS = ['tests_'] -RUNNER_PATH = './runners/test_runner' - -SUITE_PROLOGUE = """ -#include "runners/test_runner.h" -#include "bd/lfs_testbd.h" -#include <stdio.h> -""" -CASE_PROLOGUE = """ -""" -CASE_EPILOGUE = """ -""" - - -def testpath(path): - path, *_ = path.split('#', 1) - return path - -def testsuite(path): - suite = testpath(path) - suite = os.path.basename(suite) - if suite.endswith('.toml'): - suite = suite[:-len('.toml')] - return suite - -def testcase(path): - _, case, *_ = path.split('#', 2) - return '%s#%s' % (testsuite(path), case) - -# TODO move this out in other files -def openio(path, mode='r'): - if path == '-': - if 'r' in mode: - return os.fdopen(os.dup(sys.stdin.fileno()), 'r') - else: - return os.fdopen(os.dup(sys.stdout.fileno()), 'w') - else: - return open(path, mode) - -class TestCase: - # create a TestCase object from a config - def __init__(self, config, args={}): - self.name = config.pop('name') - self.path = config.pop('path') - self.suite = config.pop('suite') - self.lineno = config.pop('lineno', None) - self.if_ = config.pop('if', None) - if isinstance(self.if_, bool): - self.if_ = 'true' if self.if_ else 'false' - self.code = config.pop('code') - self.code_lineno = config.pop('code_lineno', None) - self.in_ = config.pop('in', - config.pop('suite_in', None)) - - self.normal = config.pop('normal', - config.pop('suite_normal', True)) - self.reentrant = config.pop('reentrant', - config.pop('suite_reentrant', False)) - - # figure out defines and build possible permutations - self.defines = set() - self.permutations = [] - - suite_defines = config.pop('suite_defines', {}) - if not isinstance(suite_defines, list): - suite_defines = [suite_defines] - defines = config.pop('defines', {}) - if not isinstance(defines, list): - defines = [defines] - - # build possible permutations - for suite_defines_ in suite_defines: - self.defines |= suite_defines_.keys() - for defines_ in defines: - self.defines |= defines_.keys() - self.permutations.extend(map(dict, it.product(*( - [(k, v) for v in (vs if isinstance(vs, list) else [vs])] - for k, vs in sorted( - (suite_defines_ | defines_).items()))))) - - for k in config.keys(): - print('\x1b[01;33mwarning:\x1b[m in %s, found unused key %r' - % (self.id(), k), - file=sys.stderr) - - def id(self): - return '%s#%s' % (self.suite, self.name) - - -class TestSuite: - # create a TestSuite object from a toml file - def __init__(self, path, args={}): - self.name = testsuite(path) - self.path = testpath(path) - - # load toml file and parse test cases - with open(self.path) as f: - # load tests - config = toml.load(f) - - # find line numbers - f.seek(0) - case_linenos = [] - 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*=)', - line) - if match and match.group('case'): - case_linenos.append((i+1, match.group('name'))) - elif match and match.group('code'): - code_linenos.append(i+2) - - # sort in case toml parsing did not retain order - case_linenos.sort() - - cases = config.pop('cases') - for (lineno, name), (nlineno, _) in it.zip_longest( - case_linenos, case_linenos[1:], - fillvalue=(float('inf'), None)): - code_lineno = min( - (l for l in code_linenos if l >= lineno and l < nlineno), - default=None) - cases[name]['lineno'] = lineno - cases[name]['code_lineno'] = code_lineno - - self.if_ = config.pop('if', None) - if isinstance(self.if_, bool): - self.if_ = 'true' if self.if_ else 'false' - - self.code = config.pop('code', None) - self.code_lineno = min( - (l for l in code_linenos - if not case_linenos or l < case_linenos[0][0]), - default=None) - - # a couple of these we just forward to all cases - defines = config.pop('defines', {}) - in_ = config.pop('in', None) - normal = config.pop('normal', True) - reentrant = config.pop('reentrant', False) - - self.cases = [] - for name, case in sorted(cases.items(), - key=lambda c: c[1].get('lineno')): - self.cases.append(TestCase(config={ - 'name': name, - 'path': path + (':%d' % case['lineno'] - if 'lineno' in case else ''), - 'suite': self.name, - 'suite_defines': defines, - 'suite_in': in_, - 'suite_normal': normal, - 'suite_reentrant': reentrant, - **case})) - - # combine per-case defines - self.defines = set.union(*( - set(case.defines) for case in self.cases)) - - # combine other per-case things - self.normal = any(case.normal for case in self.cases) - self.reentrant = any(case.reentrant for case in self.cases) - - for k in config.keys(): - print('\x1b[01;33mwarning:\x1b[m in %s, found unused key %r' - % (self.id(), k), - file=sys.stderr) - - def id(self): - return self.name - - - -def compile(**args): - # find .toml files - paths = [] - for path in args.get('test_ids', TEST_PATHS): - if os.path.isdir(path): - path = path + '/*.toml' - - for path in glob.glob(path): - paths.append(path) - - if not paths: - print('no test suites found in %r?' % args['test_ids']) - sys.exit(-1) - - if not args.get('source'): - if len(paths) > 1: - print('more than one test suite for compilation? (%r)' - % args['test_ids']) - sys.exit(-1) - - # load our suite - suite = TestSuite(paths[0]) - else: - # load all suites - suites = [TestSuite(path) for path in paths] - suites.sort(key=lambda s: s.name) - - # write generated test source - if 'output' in args: - with openio(args['output'], 'w') as f: - _write = f.write - def write(s): - f.lineno += s.count('\n') - _write(s) - def writeln(s=''): - f.lineno += s.count('\n') + 1 - _write(s) - _write('\n') - f.lineno = 1 - f.write = write - f.writeln = writeln - - # redirect littlefs tracing - f.writeln('#define LFS_TRACE_(fmt, ...) do { \\') - f.writeln(8*' '+'extern FILE *test_trace; \\') - f.writeln(8*' '+'if (test_trace) { \\') - f.writeln(12*' '+'fprintf(test_trace, ' - '"%s:%d:trace: " fmt "%s\\n", \\') - f.writeln(20*' '+'__FILE__, __LINE__, __VA_ARGS__); \\') - f.writeln(8*' '+'} \\') - f.writeln(4*' '+'} while (0)') - f.writeln('#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")') - f.writeln('#define LFS_TESTBD_TRACE(...) ' - 'LFS_TRACE_(__VA_ARGS__, "")') - f.writeln() - - # write out generated functions, this can end up in different - # files depending on the "in" attribute - # - # note it's up to the specific generated file to declare - # the test defines - def write_case_functions(f, suite, case): - # create case define functions - if case.defines: - # deduplicate defines by value to try to reduce the - # number of functions we generate - define_cbs = {} - for i, defines in enumerate(case.permutations): - for k, v in sorted(defines.items()): - if v not in define_cbs: - name = ('__test__%s__%s__%s__%d' - % (suite.name, case.name, k, i)) - define_cbs[v] = name - f.writeln('uintmax_t %s(void) {' % name) - f.writeln(4*' '+'return %s;' % v) - f.writeln('}') - f.writeln() - f.writeln('uintmax_t (*const *const ' - '__test__%s__%s__defines[])(void) = {' - % (suite.name, case.name)) - for defines in case.permutations: - f.writeln(4*' '+'(uintmax_t (*const[])(void)){') - for define in sorted(suite.defines): - f.writeln(8*' '+'%s,' % ( - define_cbs[defines[define]] - if define in defines - else 'NULL')) - f.writeln(4*' '+'},') - f.writeln('};') - f.writeln() - - # create case filter function - if suite.if_ is not None or case.if_ is not None: - f.writeln('bool __test__%s__%s__filter(void) {' - % (suite.name, case.name)) - f.writeln(4*' '+'return %s;' - % ' && '.join('(%s)' % if_ - for if_ in [suite.if_, case.if_] - if if_ is not None)) - f.writeln('}') - f.writeln() - - # create case run function - f.writeln('void __test__%s__%s__run(' - '__attribute__((unused)) struct lfs_config *cfg) {' - % (suite.name, case.name)) - if CASE_PROLOGUE.strip(): - f.writeln(4*' '+'%s' - % CASE_PROLOGUE.strip().replace('\n', '\n'+4*' ')) - f.writeln() - f.writeln(4*' '+'// test case %s' % case.id()) - if case.code_lineno is not None: - f.writeln(4*' '+'#line %d "%s"' - % (case.code_lineno, suite.path)) - f.write(case.code) - if case.code_lineno is not None: - f.writeln(4*' '+'#line %d "%s"' - % (f.lineno+1, args['output'])) - if CASE_EPILOGUE.strip(): - f.writeln() - f.writeln(4*' '+'%s' - % CASE_EPILOGUE.strip().replace('\n', '\n'+4*' ')) - f.writeln('}') - f.writeln() - - if not args.get('source'): - # write test suite prologue - f.writeln('%s' % SUITE_PROLOGUE.strip()) - f.writeln() - if suite.code is not None: - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (suite.code_lineno, suite.path)) - f.write(suite.code) - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (f.lineno+1, args['output'])) - f.writeln() - - if suite.defines: - for i, define in enumerate(sorted(suite.defines)): - f.writeln('#ifndef %s' % define) - f.writeln('#define %-24s test_define(%d)' - % (define, i)) - f.writeln('#endif') - f.writeln() - - for case in suite.cases: - # create case functions - if case.in_ is None: - write_case_functions(f, suite, case) - else: - if case.defines: - f.writeln('extern uintmax_t (*const *const ' - '__test__%s__%s__defines[])(void);' - % (suite.name, case.name)) - if suite.if_ is not None or case.if_ is not None: - f.writeln('extern bool __test__%s__%s__filter(' - 'void);' - % (suite.name, case.name)) - f.writeln('extern void __test__%s__%s__run(' - 'struct lfs_config *cfg);' - % (suite.name, case.name)) - f.writeln() - - # create case struct - f.writeln('const struct test_case __test__%s__%s__case = {' - % (suite.name, case.name)) - f.writeln(4*' '+'.id = "%s",' % case.id()) - f.writeln(4*' '+'.name = "%s",' % case.name) - f.writeln(4*' '+'.path = "%s",' % case.path) - f.writeln(4*' '+'.types = %s,' - % ' | '.join(filter(None, [ - 'TEST_NORMAL' if case.normal else None, - 'TEST_REENTRANT' if case.reentrant else None]))) - f.writeln(4*' '+'.permutations = %d,' - % len(case.permutations)) - if case.defines: - f.writeln(4*' '+'.defines = __test__%s__%s__defines,' - % (suite.name, case.name)) - if suite.if_ is not None or case.if_ is not None: - f.writeln(4*' '+'.filter = __test__%s__%s__filter,' - % (suite.name, case.name)) - f.writeln(4*' '+'.run = __test__%s__%s__run,' - % (suite.name, case.name)) - f.writeln('};') - f.writeln() - - # create suite define names - if suite.defines: - f.writeln('const char *const __test__%s__define_names[] = {' - % suite.name) - for k in sorted(suite.defines): - f.writeln(4*' '+'"%s",' % k) - f.writeln('};') - f.writeln() - - # create suite struct - f.writeln('const struct test_suite __test__%s__suite = {' - % suite.name) - f.writeln(4*' '+'.id = "%s",' % suite.id()) - f.writeln(4*' '+'.name = "%s",' % suite.name) - f.writeln(4*' '+'.path = "%s",' % suite.path) - f.writeln(4*' '+'.types = %s,' - % ' | '.join(filter(None, [ - 'TEST_NORMAL' if suite.normal else None, - 'TEST_REENTRANT' if suite.reentrant else None]))) - if suite.defines: - f.writeln(4*' '+'.define_names = __test__%s__define_names,' - % suite.name) - f.writeln(4*' '+'.define_count = %d,' % len(suite.defines)) - f.writeln(4*' '+'.cases = (const struct test_case *const []){') - for case in suite.cases: - f.writeln(8*' '+'&__test__%s__%s__case,' - % (suite.name, case.name)) - f.writeln(4*' '+'},') - f.writeln(4*' '+'.case_count = %d,' % len(suite.cases)) - f.writeln('};') - f.writeln() - - else: - # copy source - f.writeln('#line 1 "%s"' % args['source']) - with open(args['source']) as sf: - shutil.copyfileobj(sf, f) - f.writeln() - - f.write(SUITE_PROLOGUE) - f.writeln() - - # write any internal tests - for suite in suites: - for case in suite.cases: - if (case.in_ is not None - and os.path.normpath(case.in_) - == os.path.normpath(args['source'])): - # write defines, but note we need to undef any - # new defines since we're in someone else's file - if suite.defines: - for i, define in enumerate( - sorted(suite.defines)): - f.writeln('#ifndef %s' % define) - f.writeln('#define %-24s test_define(%d)' - % (define, i)) - f.writeln('#define __TEST__%s__NEEDS_UNDEF' - % define) - f.writeln('#endif') - f.writeln() - - write_case_functions(f, suite, case) - - if suite.defines: - for define in sorted(suite.defines): - f.writeln('#ifdef __TEST__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef __TEST__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef %s' % define) - f.writeln('#endif') - f.writeln() - - # add suite info to test_runner.c - if args['source'] == 'runners/test_runner.c': - f.writeln() - for suite in suites: - f.writeln('extern const struct test_suite ' - '__test__%s__suite;' % suite.name) - f.writeln('const struct test_suite *test_suites[] = {') - for suite in suites: - f.writeln(4*' '+'&__test__%s__suite,' % suite.name) - f.writeln('};') - f.writeln('const size_t test_suite_count = %d;' - % len(suites)) - -def runner(**args): - cmd = args['runner'].copy() - cmd.extend(args.get('test_ids')) - - # run under some external command? - cmd[:0] = args.get('exec', []) - - # run under valgrind? - if args.get('valgrind'): - cmd[:0] = filter(None, [ - 'valgrind', - '--leak-check=full', - '--track-origins=yes', - '--error-exitcode=4', - '-q']) - - # filter tests? - if args.get('normal'): cmd.append('-n') - if args.get('reentrant'): cmd.append('-r') - if args.get('geometry'): - cmd.append('-G%s' % args.get('geometry')) - - # defines? - if args.get('define'): - for define in args.get('define'): - cmd.append('-D%s' % define) - - return cmd - -def list_(**args): - cmd = runner(**args) - if args.get('summary'): cmd.append('--summary') - if args.get('list_suites'): cmd.append('--list-suites') - if args.get('list_cases'): cmd.append('--list-cases') - if args.get('list_paths'): cmd.append('--list-paths') - if args.get('list_defines'): cmd.append('--list-defines') - if args.get('list_geometries'): cmd.append('--list-geometries') - - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - sys.exit(sp.call(cmd)) - - -def find_cases(runner_, **args): - # query from runner - cmd = runner_ + ['--list-cases'] - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - stdout=sp.PIPE, - stderr=sp.PIPE if not args.get('verbose') else None, - universal_newlines=True, - errors='replace') - expected_suite_perms = co.defaultdict(lambda: 0) - expected_case_perms = co.defaultdict(lambda: 0) - expected_perms = 0 - total_perms = 0 - pattern = re.compile( - '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' - '[^\s]+\s+(?P<filtered>\d+)/(?P<perms>\d+)') - # skip the first line - for line in it.islice(proc.stdout, 1, None): - m = pattern.match(line) - if m: - filtered = int(m.group('filtered')) - expected_suite_perms[m.group('suite')] += filtered - expected_case_perms[m.group('id')] += filtered - expected_perms += filtered - total_perms += int(m.group('perms')) - proc.wait() - if proc.returncode != 0: - if not args.get('verbose'): - for line in proc.stderr: - sys.stdout.write(line) - sys.exit(-1) - - return ( - expected_suite_perms, - expected_case_perms, - expected_perms, - total_perms) - -def find_paths(runner_, **args): - # query from runner - cmd = runner_ + ['--list-paths'] - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - stdout=sp.PIPE, - stderr=sp.PIPE if not args.get('verbose') else None, - universal_newlines=True, - errors='replace') - paths = co.OrderedDict() - pattern = re.compile( - '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' - '(?P<path>[^:]+):(?P<lineno>\d+)') - for line in proc.stdout: - m = pattern.match(line) - if m: - paths[m.group('id')] = (m.group('path'), int(m.group('lineno'))) - proc.wait() - if proc.returncode != 0: - if not args.get('verbose'): - for line in proc.stderr: - sys.stdout.write(line) - sys.exit(-1) - - return paths - -def find_defines(runner_, **args): - # query from runner - cmd = runner_ + ['--list-defines'] - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - stdout=sp.PIPE, - stderr=sp.PIPE if not args.get('verbose') else None, - universal_newlines=True, - errors='replace') - defines = co.OrderedDict() - pattern = re.compile( - '^(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)\s+' - '(?P<defines>(?:\w+=\w+\s*)+)') - for line in proc.stdout: - m = pattern.match(line) - if m: - defines[m.group('id')] = {k: v - for k, v in re.findall('(\w+)=(\w+)', m.group('defines'))} - proc.wait() - if proc.returncode != 0: - if not args.get('verbose'): - for line in proc.stderr: - sys.stdout.write(line) - sys.exit(-1) - - return defines - - -class TestFailure(Exception): - def __init__(self, id, returncode, output, assert_=None): - self.id = id - self.returncode = returncode - self.output = output - self.assert_ = assert_ - -def run_stage(name, runner_, **args): - # get expected suite/case/perm counts - expected_suite_perms, expected_case_perms, expected_perms, total_perms = ( - find_cases(runner_, **args)) - - passed_suite_perms = co.defaultdict(lambda: 0) - passed_case_perms = co.defaultdict(lambda: 0) - passed_perms = 0 - failures = [] - killed = False - - pattern = re.compile('^(?:' - '(?P<op>running|finished|skipped) ' - '(?P<id>(?P<case>(?P<suite>[^#]+)#[^\s#]+)[^\s]*)' - '|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):' - ' *(?P<message>.*)' ')$') - locals = th.local() - children = set() - - def run_runner(runner_): - nonlocal passed_suite_perms - nonlocal passed_case_perms - nonlocal passed_perms - nonlocal locals - - # run the tests! - cmd = runner_.copy() - if args.get('disk'): - cmd.append('--disk=%s' % args['disk']) - if args.get('trace'): - cmd.append('--trace=%s' % args['trace']) - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - mpty, spty = pty.openpty() - proc = sp.Popen(cmd, stdout=spty, stderr=spty) - os.close(spty) - children.add(proc) - mpty = os.fdopen(mpty, 'r', 1) - if args.get('output'): - output = openio(args['output'], 'w') - - last_id = None - last_output = [] - last_assert = None - try: - while True: - # parse a line for state changes - try: - line = mpty.readline() - except OSError as e: - if e.errno == errno.EIO: - break - raise - if not line: - break - last_output.append(line) - if args.get('output'): - output.write(line) - elif args.get('verbose'): - sys.stdout.write(line) - - m = pattern.match(line) - if m: - op = m.group('op') or m.group('op_') - if op == 'running': - locals.seen_perms += 1 - last_id = m.group('id') - last_output = [] - last_assert = None - elif op == 'finished': - passed_suite_perms[m.group('suite')] += 1 - passed_case_perms[m.group('case')] += 1 - passed_perms += 1 - elif op == 'skipped': - locals.seen_perms += 1 - elif op == 'assert': - last_assert = ( - m.group('path'), - int(m.group('lineno')), - m.group('message')) - # go ahead and kill the process, aborting takes a while - if args.get('keep_going'): - proc.kill() - except KeyboardInterrupt: - raise TestFailure(last_id, 1, last_output) - finally: - children.remove(proc) - mpty.close() - if args.get('output'): - output.close() - - proc.wait() - if proc.returncode != 0: - raise TestFailure( - last_id, - proc.returncode, - last_output, - last_assert) - - def run_job(runner, start=None, step=None): - nonlocal failures - nonlocal locals - - start = start or 0 - step = step or 1 - while start < total_perms: - runner_ = runner.copy() - if start is not None: - runner_.append('--start=%d' % start) - if step is not None: - runner_.append('--step=%d' % step) - if args.get('isolate') or args.get('valgrind'): - runner_.append('--stop=%d' % (start+step)) - - try: - # run the tests - locals.seen_perms = 0 - run_runner(runner_) - assert locals.seen_perms > 0 - start += locals.seen_perms*step - - except TestFailure as failure: - # race condition for multiple failures? - if failures and not args.get('keep_going'): - break - - failures.append(failure) - - if args.get('keep_going') and not killed: - # resume after failed test - assert locals.seen_perms > 0 - start += locals.seen_perms*step - continue - else: - # stop other tests - for child in children.copy(): - child.kill() - - - # parallel jobs? - runners = [] - if 'jobs' in args: - for job in range(args['jobs']): - runners.append(th.Thread( - target=run_job, args=(runner_, job, args['jobs']))) - else: - runners.append(th.Thread( - target=run_job, args=(runner_, None, None))) - - for r in runners: - r.start() - - needs_newline = False - try: - while any(r.is_alive() for r in runners): - time.sleep(0.01) - - if not args.get('verbose'): - sys.stdout.write('\r\x1b[K' - 'running \x1b[%dm%s:\x1b[m %s ' - % (32 if not failures else 31, - name, - ', '.join(filter(None, [ - '%d/%d suites' % ( - sum(passed_suite_perms[k] == v - for k, v in expected_suite_perms.items()), - len(expected_suite_perms)) - if (not args.get('by_suites') - and not args.get('by_cases')) else None, - '%d/%d cases' % ( - sum(passed_case_perms[k] == v - for k, v in expected_case_perms.items()), - len(expected_case_perms)) - if not args.get('by_cases') else None, - '%d/%d perms' % (passed_perms, expected_perms), - '\x1b[31m%d/%d failures\x1b[m' - % (len(failures), expected_perms) - if failures else None])))) - sys.stdout.flush() - needs_newline = True - except KeyboardInterrupt: - # this is handled by the runner threads, we just - # need to not abort here - killed = True - finally: - if needs_newline: - print() - - for r in runners: - r.join() - - return ( - expected_perms, - passed_perms, - failures, - killed) - - -def run(**args): - start = time.time() - - runner_ = runner(**args) - print('using runner: %s' - % ' '.join(shlex.quote(c) for c in runner_)) - expected_suite_perms, expected_case_perms, expected_perms, total_perms = ( - find_cases(runner_, **args)) - print('found %d suites, %d cases, %d/%d permutations' - % (len(expected_suite_perms), - len(expected_case_perms), - expected_perms, - total_perms)) - print() - - expected = 0 - passed = 0 - failures = [] - for type, by in it.product( - ['normal', 'reentrant'], - expected_case_perms.keys() if args.get('by_cases') - else expected_suite_perms.keys() if args.get('by_suites') - else [None]): - # rebuild runner for each stage to override test identifier if needed - stage_runner = runner(**args | { - 'test_ids': [by] if by is not None else args.get('test_ids', []), - 'normal': type == 'normal', - 'reentrant': type == 'reentrant'}) - - # spawn jobs for stage - expected_, passed_, failures_, killed = run_stage( - '%s %s' % (type, by or 'tests'), stage_runner, **args) - expected += expected_ - passed += passed_ - failures.extend(failures_) - if (failures and not args.get('keep_going')) or killed: - break - - # show summary - print() - print('\x1b[%dmdone:\x1b[m %d/%d passed, %d/%d failed, in %.2fs' - % (32 if not failures else 31, - passed, expected, len(failures), expected, - time.time()-start)) - print() - - # print each failure - if failures: - # get some extra info from runner - runner_paths = find_paths(runner_, **args) - runner_defines = find_defines(runner_, **args) - - for failure in failures: - # show summary of failure - path, lineno = runner_paths[testcase(failure.id)] - defines = runner_defines[failure.id] - - print('\x1b[01m%s:%d:\x1b[01;31mfailure:\x1b[m %s%s failed' - % (path, lineno, failure.id, - ' (%s)' % ', '.join( - '%s=%s' % (k, v) for k, v in defines.items()) - if defines else '')) - - if failure.output: - output = failure.output - if failure.assert_ is not None: - output = output[:-1] - for line in output[-5:]: - sys.stdout.write(line) - - if failure.assert_ is not None: - path, lineno, message = failure.assert_ - print('\x1b[01m%s:%d:\x1b[01;31massert:\x1b[m %s' - % (path, lineno, message)) - with open(path) as f: - line = next(it.islice(f, lineno-1, None)).strip('\n') - print(line) - print() - - # drop into gdb? - if failures and (args.get('gdb') - or args.get('gdb_case') - or args.get('gdb_main')): - failure = failures[0] - runner_ = runner(**args | {'test_ids': [failure.id]}) - - if args.get('gdb_main'): - cmd = ['gdb', - '-ex', 'break main', - '-ex', 'run', - '--args'] + runner_ - elif args.get('gdb_case'): - path, lineno = runner_paths[testcase(failure.id)] - cmd = ['gdb', - '-ex', 'break %s:%d' % (path, lineno), - '-ex', 'run', - '--args'] + runner_ - elif failure.assert_ is not None: - cmd = ['gdb', - '-ex', 'run', - '-ex', 'frame function raise', - '-ex', 'up 2', - '--args'] + runner_ - else: - cmd = ['gdb', - '-ex', 'run', - '--args'] + runner_ - - # exec gdb interactively - if args.get('verbose'): - print(' '.join(shlex.quote(c) for c in cmd)) - os.execvp(cmd[0], cmd) - - return 1 if failures else 0 - - -def main(**args): - if args.get('compile'): - compile(**args) - elif (args.get('summary') - or args.get('list_suites') - or args.get('list_cases') - or args.get('list_paths') - or args.get('list_defines') - or args.get('list_geometries') - or args.get('list_defaults')): - list_(**args) - else: - run(**args) - - -if __name__ == "__main__": - import argparse - import sys - parser = argparse.ArgumentParser( - description="Build and run tests.", - conflict_handler='resolve') - parser.add_argument('test_ids', nargs='*', - help="Description of testis to run. May be a directory, path, or \ - test identifier. Test identifiers are of the form \ - <suite_name>#<case_name>#<permutation>, but suffixes can be \ - dropped to run any matching tests. Defaults to %r." % TEST_PATHS) - parser.add_argument('-v', '--verbose', action='store_true', - help="Output commands that run behind the scenes.") - # test flags - test_parser = parser.add_argument_group('test options') - test_parser.add_argument('-Y', '--summary', action='store_true', - help="Show quick summary.") - test_parser.add_argument('-l', '--list-suites', action='store_true', - help="List test suites.") - test_parser.add_argument('-L', '--list-cases', action='store_true', - help="List test cases.") - test_parser.add_argument('--list-paths', action='store_true', - help="List the path for each test case.") - test_parser.add_argument('--list-defines', action='store_true', - help="List the defines for each test permutation.") - test_parser.add_argument('--list-geometries', action='store_true', - help="List the disk geometries used for testing.") - test_parser.add_argument('--list-defaults', action='store_true', - help="List the default defines in this test-runner.") - test_parser.add_argument('-D', '--define', action='append', - help="Override a test define.") - test_parser.add_argument('-G', '--geometry', - help="Filter by geometry.") - test_parser.add_argument('-n', '--normal', action='store_true', - help="Filter for normal tests. Can be combined.") - test_parser.add_argument('-r', '--reentrant', action='store_true', - help="Filter for reentrant tests. Can be combined.") - test_parser.add_argument('-d', '--disk', - help="Use this file as the disk.") - test_parser.add_argument('-t', '--trace', - help="Redirect trace output to this file.") - test_parser.add_argument('-o', '--output', - help="Redirect stdout and stderr to this file.") - test_parser.add_argument('--runner', default=[RUNNER_PATH], - type=lambda x: x.split(), - help="Path to runner, defaults to %r" % RUNNER_PATH) - test_parser.add_argument('-j', '--jobs', nargs='?', type=int, - const=len(os.sched_getaffinity(0)), - help="Number of parallel runners to run.") - test_parser.add_argument('-k', '--keep-going', action='store_true', - help="Don't stop on first error.") - test_parser.add_argument('-i', '--isolate', action='store_true', - help="Run each test permutation in a separate process.") - test_parser.add_argument('-b', '--by-suites', action='store_true', - help="Step through tests by suite.") - test_parser.add_argument('-B', '--by-cases', action='store_true', - help="Step through tests by case.") - test_parser.add_argument('--gdb', action='store_true', - help="Drop into gdb on test failure.") - test_parser.add_argument('--gdb-case', action='store_true', - help="Drop into gdb on test failure but stop at the beginning \ - of the failing test case.") - test_parser.add_argument('--gdb-main', action='store_true', - help="Drop into gdb on test failure but stop at the beginning \ - of main.") - test_parser.add_argument('--valgrind', action='store_true', - help="Run under Valgrind to find memory errors. Implicitly sets \ - --isolate.") - test_parser.add_argument('--exec', default=[], type=lambda e: e.split(), - help="Run under another executable.") - # compilation flags - comp_parser = parser.add_argument_group('compilation options') - comp_parser.add_argument('-c', '--compile', action='store_true', - help="Compile a test suite or source file.") - comp_parser.add_argument('-s', '--source', - help="Source file to compile, possibly injecting internal tests.") - comp_parser.add_argument('-o', '--output', - help="Output file.") - # TODO apply this to other scripts? - sys.exit(main(**{k: v - for k, v in vars(parser.parse_args()).items() - if v is not None})) diff --git a/tests/test_alloc.toml b/tests/test_alloc.toml index fa92da5..4e43db3 100644 --- a/tests/test_alloc.toml +++ b/tests/test_alloc.toml @@ -1,27 +1,30 @@ # allocator tests # note for these to work there are a number constraints on the device geometry -if = 'LFS_BLOCK_CYCLES == -1' +if = 'BLOCK_CYCLES == -1' -[[case]] # parallel allocation test -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' +# parallel allocation test +[cases.parallel_allocation] +defines.FILES = 3 +defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' code = ''' - const char *names[FILES] = {"bacon", "eggs", "pancakes"}; + const char *names[] = {"bacon", "eggs", "pancakes"}; lfs_file_t files[FILES]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "breakfast") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); lfs_file_open(&lfs, &files[n], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; } for (int n = 0; n < FILES; n++) { - size = strlen(names[n]); + size_t size = strlen(names[n]); for (lfs_size_t i = 0; i < SIZE; i += size) { lfs_file_write(&lfs, &files[n], names[n], size) => size; } @@ -31,12 +34,15 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); for (lfs_size_t i = 0; i < SIZE; i += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; assert(memcmp(buffer, names[n], size) == 0); } @@ -45,23 +51,28 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # serial allocation test -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' +# serial allocation test +[cases.serial_allocation] +defines.FILES = 3 +defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' code = ''' - const char *names[FILES] = {"bacon", "eggs", "pancakes"}; + const char *names[] = {"bacon", "eggs", "pancakes"}; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "breakfast") => 0; lfs_unmount(&lfs) => 0; for (int n = 0; n < FILES; n++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); + uint8_t buffer[1024]; memcpy(buffer, names[n], size); for (int i = 0; i < SIZE; i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; @@ -70,12 +81,15 @@ code = ''' lfs_unmount(&lfs) => 0; } - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); for (int i = 0; i < SIZE; i += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; assert(memcmp(buffer, names[n], size) == 0); } @@ -84,29 +98,32 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # parallel allocation reuse test -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' -define.CYCLES = [1, 10] +# parallel allocation reuse test +[cases.parallel_allocation_reuse] +defines.FILES = 3 +defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' +defines.CYCLES = [1, 10] code = ''' - const char *names[FILES] = {"bacon", "eggs", "pancakes"}; + const char *names[] = {"bacon", "eggs", "pancakes"}; lfs_file_t files[FILES]; - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; for (int c = 0; c < CYCLES; c++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "breakfast") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); lfs_file_open(&lfs, &files[n], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; } for (int n = 0; n < FILES; n++) { - size = strlen(names[n]); + size_t size = strlen(names[n]); for (int i = 0; i < SIZE; i += size) { lfs_file_write(&lfs, &files[n], names[n], size) => size; } @@ -116,12 +133,15 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); for (int i = 0; i < SIZE; i += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; assert(memcmp(buffer, names[n], size) == 0); } @@ -129,8 +149,9 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); lfs_remove(&lfs, path) => 0; } @@ -139,26 +160,31 @@ code = ''' } ''' -[[case]] # serial allocation reuse test -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)' -define.CYCLES = [1, 10] +# serial allocation reuse test +[cases.serial_allocation_reuse] +defines.FILES = 3 +defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' +defines.CYCLES = [1, 10] code = ''' - const char *names[FILES] = {"bacon", "eggs", "pancakes"}; + const char *names[] = {"bacon", "eggs", "pancakes"}; - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; for (int c = 0; c < CYCLES; c++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "breakfast") => 0; lfs_unmount(&lfs) => 0; for (int n = 0; n < FILES; n++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); + uint8_t buffer[1024]; memcpy(buffer, names[n], size); for (int i = 0; i < SIZE; i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; @@ -167,12 +193,15 @@ code = ''' lfs_unmount(&lfs) => 0; } - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; - size = strlen(names[n]); + size_t size = strlen(names[n]); for (int i = 0; i < SIZE; i += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; assert(memcmp(buffer, names[n], size) == 0); } @@ -180,8 +209,9 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int n = 0; n < FILES; n++) { + char path[1024]; sprintf(path, "breakfast/%s", names[n]); lfs_remove(&lfs, path) => 0; } @@ -190,12 +220,16 @@ code = ''' } ''' -[[case]] # exhaustion test +# exhaustion test +[cases.exhaustion] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); - size = strlen("exhaustion"); + size_t size = strlen("exhaustion"); + uint8_t buffer[1024]; memcpy(buffer, "exhaustion", size); lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_sync(&lfs, &file) => 0; @@ -216,7 +250,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); size = strlen("exhaustion"); lfs_file_size(&lfs, &file) => size; @@ -226,14 +260,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # exhaustion wraparound test -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)' +# exhaustion wraparound test +[cases.exhaustion_wraparound] +defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-4)) / 3)' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT); - size = strlen("buffering"); + size_t size = strlen("buffering"); + uint8_t buffer[1024]; memcpy(buffer, "buffering", size); for (int i = 0; i < SIZE; i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; @@ -263,7 +301,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); size = strlen("exhaustion"); lfs_file_size(&lfs, &file) => size; @@ -274,17 +312,22 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # dir exhaustion test +# dir exhaustion test +[cases.dir_exhaustion] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // find out max file size lfs_mkdir(&lfs, "exhaustiondir") => 0; - size = strlen("blahblahblahblah"); + size_t size = strlen("blahblahblahblah"); + uint8_t buffer[1024]; memcpy(buffer, "blahblahblahblah", size); + lfs_file_t file; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); int count = 0; + int err; while (true) { err = lfs_file_write(&lfs, &file, buffer, size); if (err < 0) { @@ -323,17 +366,21 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # what if we have a bad block during an allocation scan? +# what if we have a bad block during an allocation scan? +[cases.bad_block_allocation] in = "lfs.c" -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR' +defines.ERASE_CYCLES = 0xffffffff +defines.BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // first fill to exhaustion to find available space + lfs_file_t file; lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "waka"); - size = strlen("waka"); + size_t size = strlen("waka"); lfs_size_t filesize = 0; while (true) { lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); @@ -345,7 +392,7 @@ code = ''' } lfs_file_close(&lfs, &file) => 0; // now fill all but a couple of blocks of the filesystem with data - filesize -= 3*LFS_BLOCK_SIZE; + filesize -= 3*BLOCK_SIZE; lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; strcpy((char*)buffer, "waka"); size = strlen("waka"); @@ -358,11 +405,11 @@ code = ''' lfs_unmount(&lfs) => 0; // remount to force an alloc scan - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // but mark the head of our file as a "bad block", this is force our // scan to bail early - lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; + lfs_testbd_setwear(cfg, fileblock, 0xffffffff) => 0; lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; strcpy((char*)buffer, "chomp"); size = strlen("chomp"); @@ -377,7 +424,7 @@ code = ''' // now reverse the "bad block" and try to write the file again until we // run out of space - lfs_testbd_setwear(&cfg, fileblock, 0) => 0; + lfs_testbd_setwear(cfg, fileblock, 0) => 0; lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; strcpy((char*)buffer, "chomp"); size = strlen("chomp"); @@ -393,7 +440,7 @@ code = ''' lfs_unmount(&lfs) => 0; // check that the disk isn't hurt - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0; strcpy((char*)buffer, "waka"); size = strlen("waka"); @@ -411,24 +458,29 @@ code = ''' # on the geometry of the block device. But they are valuable. Eventually they # should be removed and replaced with generalized tests. -[[case]] # chained dir exhaustion test -define.LFS_BLOCK_SIZE = 512 -define.LFS_BLOCK_COUNT = 1024 -if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' +# chained dir exhaustion test +[cases.chained_dir_exhaustion] +if = 'BLOCK_SIZE == 512' +defines.BLOCK_COUNT = 1024 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // find out max file size lfs_mkdir(&lfs, "exhaustiondir") => 0; for (int i = 0; i < 10; i++) { + char path[1024]; sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_mkdir(&lfs, path) => 0; } - size = strlen("blahblahblahblah"); + size_t size = strlen("blahblahblahblah"); + uint8_t buffer[1024]; memcpy(buffer, "blahblahblahblah", size); + lfs_file_t file; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); int count = 0; + int err; while (true) { err = lfs_file_write(&lfs, &file, buffer, size); if (err < 0) { @@ -443,6 +495,7 @@ code = ''' lfs_remove(&lfs, "exhaustion") => 0; lfs_remove(&lfs, "exhaustiondir") => 0; for (int i = 0; i < 10; i++) { + char path[1024]; sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_remove(&lfs, path) => 0; } @@ -455,6 +508,7 @@ code = ''' lfs_file_sync(&lfs, &file) => 0; for (int i = 0; i < 10; i++) { + char path[1024]; sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); lfs_mkdir(&lfs, path) => 0; } @@ -482,27 +536,31 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # split dir test -define.LFS_BLOCK_SIZE = 512 -define.LFS_BLOCK_COUNT = 1024 -if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' +# split dir test +[cases.split_dir] +if = 'BLOCK_SIZE == 512' +defines.BLOCK_COUNT = 1024 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // create one block hole for half a directory + lfs_file_t file; lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; - for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { + for (lfs_size_t i = 0; i < cfg->block_size; i += 2) { + uint8_t buffer[1024]; memcpy(&buffer[i], "hi", 2); } - lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size; + uint8_t buffer[1024]; + lfs_file_write(&lfs, &file, buffer, cfg->block_size) => cfg->block_size; lfs_file_close(&lfs, &file) => 0; lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); - size = strlen("blahblahblahblah"); + size_t size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < (cfg.block_count-4)*(cfg.block_size-8); + i < (cfg->block_count-4)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -510,7 +568,7 @@ code = ''' // remount to force reset of lookahead lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // open hole lfs_remove(&lfs, "bump") => 0; @@ -518,30 +576,33 @@ code = ''' lfs_mkdir(&lfs, "splitdir") => 0; lfs_file_open(&lfs, &file, "splitdir/bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; - for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { + for (lfs_size_t i = 0; i < cfg->block_size; i += 2) { memcpy(&buffer[i], "hi", 2); } - lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC; + lfs_file_write(&lfs, &file, buffer, 2*cfg->block_size) => LFS_ERR_NOSPC; lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -[[case]] # outdated lookahead test -define.LFS_BLOCK_SIZE = 512 -define.LFS_BLOCK_COUNT = 1024 -if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' +# outdated lookahead test +[cases.outdated_lookahead] +if = 'BLOCK_SIZE == 512' +defines.BLOCK_COUNT = 1024 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // fill completely with two files + lfs_file_t file; lfs_file_open(&lfs, &file, "exhaustion1", LFS_O_WRONLY | LFS_O_CREAT) => 0; - size = strlen("blahblahblahblah"); + size_t size = strlen("blahblahblahblah"); + uint8_t buffer[1024]; memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -552,7 +613,7 @@ code = ''' size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -560,7 +621,7 @@ code = ''' // remount to force reset of lookahead lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // rewrite one file lfs_file_open(&lfs, &file, "exhaustion1", @@ -569,7 +630,7 @@ code = ''' size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -583,7 +644,7 @@ code = ''' size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -592,21 +653,24 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # outdated lookahead and split dir test -define.LFS_BLOCK_SIZE = 512 -define.LFS_BLOCK_COUNT = 1024 -if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024' +# outdated lookahead and split dir test +[cases.outdated_lookahead_split_dir] +if = 'BLOCK_SIZE == 512' +defines.BLOCK_COUNT = 1024 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // fill completely with two files + lfs_file_t file; lfs_file_open(&lfs, &file, "exhaustion1", LFS_O_WRONLY | LFS_O_CREAT) => 0; - size = strlen("blahblahblahblah"); + size_t size = strlen("blahblahblahblah"); + uint8_t buffer[1024]; memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -617,7 +681,7 @@ code = ''' size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); + i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -625,7 +689,7 @@ code = ''' // remount to force reset of lookahead lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // rewrite one file with a hole of one block lfs_file_open(&lfs, &file, "exhaustion1", @@ -634,7 +698,7 @@ code = ''' size = strlen("blahblahblahblah"); memcpy(buffer, "blahblahblahblah", size); for (lfs_size_t i = 0; - i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); + i < ((cfg->block_count-2)/2 - 1)*(cfg->block_size-8); i += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } diff --git a/tests/test_attrs.toml b/tests/test_attrs.toml index db8d0c7..719ea68 100644 --- a/tests/test_attrs.toml +++ b/tests/test_attrs.toml @@ -1,14 +1,17 @@ -[[case]] # set/get attribute +[cases.get_set_attrs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hello") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); lfs_file_close(&lfs, &file); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + uint8_t buffer[1024]; memset(buffer, 0, sizeof(buffer)); lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0; lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0; @@ -60,7 +63,7 @@ code = ''' lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; memset(buffer, 0, sizeof(buffer)); lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9; @@ -76,17 +79,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # set/get root attribute +[cases.get_set_root_attrs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hello") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); lfs_file_close(&lfs, &file); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + uint8_t buffer[1024]; memset(buffer, 0, sizeof(buffer)); lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0; lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0; @@ -137,7 +143,7 @@ code = ''' lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; memset(buffer, 0, sizeof(buffer)); lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9; @@ -153,17 +159,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # set/get file attribute +[cases.get_set_file_attrs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hello") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); lfs_file_close(&lfs, &file); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + uint8_t buffer[1024]; memset(buffer, 0, sizeof(buffer)); struct lfs_attr attrs1[] = { {'A', buffer, 4}, @@ -238,7 +247,7 @@ code = ''' lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; memset(buffer, 0, sizeof(buffer)); struct lfs_attr attrs3[] = { {'A', buffer, 4}, @@ -260,20 +269,23 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # deferred file attributes +[cases.deferred_file_attrs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hello") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); lfs_file_close(&lfs, &file); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_setattr(&lfs, "hello/hello", 'B', "fffffffff", 9) => 0; lfs_setattr(&lfs, "hello/hello", 'C', "ccccc", 5) => 0; + uint8_t buffer[1024]; memset(buffer, 0, sizeof(buffer)); struct lfs_attr attrs1[] = { {'B', "gggg", 4}, diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index 06967a6..c5cab47 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -1,28 +1,30 @@ # bad blocks with block cycles should be tested in test_relocations -if = 'LFS_BLOCK_CYCLES == -1' +if = '(int32_t)BLOCK_CYCLES == -1' -[[case]] # single bad blocks -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_ERASE_VALUE = [0x00, 0xff, -1] -define.LFS_BADBLOCK_BEHAVIOR = [ +[cases.single_bad_blocks] +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.ERASE_CYCLES = 0xffffffff +defines.ERASE_VALUE = [0x00, 0xff, -1] +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', 'LFS_TESTBD_BADBLOCK_PROGNOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] -define.NAMEMULT = 64 -define.FILEMULT = 1 +defines.NAMEMULT = 64 +defines.FILEMULT = 1 code = ''' - for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { - lfs_testbd_setwear(&cfg, badblock-1, 0) => 0; - lfs_testbd_setwear(&cfg, badblock, 0xffffffff) => 0; - - lfs_format(&lfs, &cfg) => 0; + for (lfs_block_t badblock = 2; badblock < BLOCK_COUNT; badblock++) { + lfs_testbd_setwear(cfg, badblock-1, 0) => 0; + lfs_testbd_setwear(cfg, badblock, 0xffffffff) => 0; + + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } @@ -34,10 +36,11 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_WRONLY | LFS_O_CREAT) => 0; - size = NAMEMULT; + lfs_size_t size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -46,12 +49,14 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } buffer[NAMEMULT] = '\0'; + struct lfs_info info; lfs_stat(&lfs, (char*)buffer, &info) => 0; info.type => LFS_TYPE_DIR; @@ -60,9 +65,10 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; - size = NAMEMULT; + int size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { uint8_t rbuffer[1024]; lfs_file_read(&lfs, &file, rbuffer, size) => size; @@ -75,28 +81,30 @@ code = ''' } ''' -[[case]] # region corruption (causes cascading failures) -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_ERASE_VALUE = [0x00, 0xff, -1] -define.LFS_BADBLOCK_BEHAVIOR = [ +[cases.region_corruption] # (causes cascading failures) +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.ERASE_CYCLES = 0xffffffff +defines.ERASE_VALUE = [0x00, 0xff, -1] +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', 'LFS_TESTBD_BADBLOCK_PROGNOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] -define.NAMEMULT = 64 -define.FILEMULT = 1 +defines.NAMEMULT = 64 +defines.FILEMULT = 1 code = ''' - for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { - lfs_testbd_setwear(&cfg, i+2, 0xffffffff) => 0; + for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) { + lfs_testbd_setwear(cfg, i+2, 0xffffffff) => 0; } - - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } @@ -108,10 +116,11 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_WRONLY | LFS_O_CREAT) => 0; - size = NAMEMULT; + lfs_size_t size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -120,12 +129,14 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } buffer[NAMEMULT] = '\0'; + struct lfs_info info; lfs_stat(&lfs, (char*)buffer, &info) => 0; info.type => LFS_TYPE_DIR; @@ -134,9 +145,10 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; - size = NAMEMULT; + lfs_size_t size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { uint8_t rbuffer[1024]; lfs_file_read(&lfs, &file, rbuffer, size) => size; @@ -148,28 +160,30 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # alternating corruption (causes cascading failures) -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_ERASE_VALUE = [0x00, 0xff, -1] -define.LFS_BADBLOCK_BEHAVIOR = [ +[cases.alternating_corruption] # (causes cascading failures) +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.ERASE_CYCLES = 0xffffffff +defines.ERASE_VALUE = [0x00, 0xff, -1] +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', 'LFS_TESTBD_BADBLOCK_PROGNOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] -define.NAMEMULT = 64 -define.FILEMULT = 1 +defines.NAMEMULT = 64 +defines.FILEMULT = 1 code = ''' - for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { - lfs_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0; + for (lfs_block_t i = 0; i < (BLOCK_COUNT-2)/2; i++) { + lfs_testbd_setwear(cfg, (2*i) + 2, 0xffffffff) => 0; } - - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } @@ -181,10 +195,11 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_WRONLY | LFS_O_CREAT) => 0; - size = NAMEMULT; + lfs_size_t size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -193,12 +208,14 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 1; i < 10; i++) { + uint8_t buffer[1024]; for (int j = 0; j < NAMEMULT; j++) { buffer[j] = '0'+i; } buffer[NAMEMULT] = '\0'; + struct lfs_info info; lfs_stat(&lfs, (char*)buffer, &info) => 0; info.type => LFS_TYPE_DIR; @@ -207,9 +224,10 @@ code = ''' buffer[j+NAMEMULT+1] = '0'+i; } buffer[2*NAMEMULT+1] = '\0'; + lfs_file_t file; lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; - size = NAMEMULT; + lfs_size_t size = NAMEMULT; for (int j = 0; j < i*FILEMULT; j++) { uint8_t rbuffer[1024]; lfs_file_read(&lfs, &file, rbuffer, size) => size; @@ -222,10 +240,10 @@ code = ''' ''' # other corner cases -[[case]] # bad superblocks (corrupt 1 or 0) -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_ERASE_VALUE = [0x00, 0xff, -1] -define.LFS_BADBLOCK_BEHAVIOR = [ +[cases.bad_superblocks] # (corrupt 1 or 0) +defines.ERASE_CYCLES = 0xffffffff +defines.ERASE_VALUE = [0x00, 0xff, -1] +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', @@ -233,9 +251,10 @@ define.LFS_BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] code = ''' - lfs_testbd_setwear(&cfg, 0, 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, 1, 0xffffffff) => 0; + lfs_testbd_setwear(cfg, 0, 0xffffffff) => 0; + lfs_testbd_setwear(cfg, 1, 0xffffffff) => 0; - lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC; - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_t lfs; + lfs_format(&lfs, cfg) => LFS_ERR_NOSPC; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' diff --git a/tests/test_dirs.toml b/tests/test_dirs.toml index 270f4f8..60346c0 100644 --- a/tests/test_dirs.toml +++ b/tests/test_dirs.toml @@ -1,8 +1,11 @@ -[[case]] # root +[cases.root] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -14,20 +17,25 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many directory creation -define.N = 'range(0, 100, 3)' +[cases.many_dir_creation] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "dir%03d", i); lfs_mkdir(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -35,6 +43,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "dir%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -45,20 +54,25 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many directory removal -define.N = 'range(3, 100, 11)' +[cases.many_dir_removal] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); lfs_mkdir(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -66,6 +80,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -75,14 +90,15 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); lfs_remove(&lfs, path) => 0; } lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -95,20 +111,25 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many directory rename -define.N = 'range(3, 100, 11)' +[cases.many_dir_rename] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "test%03d", i); lfs_mkdir(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -116,6 +137,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "test%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -125,7 +147,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { char oldpath[128]; char newpath[128]; @@ -135,7 +157,7 @@ code = ''' } lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -144,6 +166,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "tedd%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -154,29 +177,34 @@ code = ''' lfs_unmount(&lfs); ''' -[[case]] # reentrant many directory creation/rename/removal -define.N = [5, 11] +[cases.reentrant_many_dir] +defines.N = [5, 11] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hi%03d", i); err = lfs_mkdir(&lfs, path); assert(err == 0 || err == LFS_ERR_EXIST); } for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); err = lfs_remove(&lfs, path); assert(err == 0 || err == LFS_ERR_NOENT); } + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -184,6 +212,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hi%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -209,6 +238,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -218,6 +248,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); lfs_remove(&lfs, path) => 0; } @@ -234,22 +265,28 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # file creation -define.N = 'range(3, 100, 11)' +[cases.file_creation] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "file%03d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -257,6 +294,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "file%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -267,22 +305,28 @@ code = ''' lfs_unmount(&lfs); ''' -[[case]] # file removal -define.N = 'range(0, 100, 3)' +[cases.file_removal] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -290,6 +334,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -299,14 +344,15 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "removeme%03d", i); lfs_remove(&lfs, path) => 0; } lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -319,22 +365,28 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # file rename -define.N = 'range(0, 100, 3)' +[cases.file_rename] +defines.N = [3,6,9,12,21,33,57,66,72,93,99] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "test%03d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -342,6 +394,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "test%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -351,7 +404,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { char oldpath[128]; char newpath[128]; @@ -361,7 +414,7 @@ code = ''' } lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -370,6 +423,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "tedd%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -380,29 +434,36 @@ code = ''' lfs_unmount(&lfs); ''' -[[case]] # reentrant file creation/rename/removal -define.N = [5, 25] +[cases.reentrant_files] +defines.N = [5, 25] +if = 'N < BLOCK_COUNT/2' reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hi%03d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_close(&lfs, &file) => 0; } for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); err = lfs_remove(&lfs, path); assert(err == 0 || err == LFS_ERR_NOENT); } + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -410,6 +471,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hi%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -435,6 +497,7 @@ code = ''' assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, "..") == 0); for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_REG); @@ -444,6 +507,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; for (int i = 0; i < N; i++) { + char path[1024]; sprintf(path, "hello%03d", i); lfs_remove(&lfs, path) => 0; } @@ -460,24 +524,28 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # nested directories +[cases.nested_dirs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "potato") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "burito", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "potato/baked") => 0; lfs_mkdir(&lfs, "potato/sweet") => 0; lfs_mkdir(&lfs, "potato/fried") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "potato") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); info.type => LFS_TYPE_DIR; @@ -498,21 +566,21 @@ code = ''' lfs_unmount(&lfs) => 0; // try removing? - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY; lfs_unmount(&lfs) => 0; // try renaming? - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "potato", "coldpotato") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "coldpotato", "warmpotato") => 0; lfs_rename(&lfs, "warmpotato", "hotpotato") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "potato") => LFS_ERR_NOENT; lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT; lfs_remove(&lfs, "warmpotato") => LFS_ERR_NOENT; @@ -520,7 +588,7 @@ code = ''' lfs_unmount(&lfs) => 0; // try cross-directory renaming - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "coldpotato") => 0; lfs_rename(&lfs, "hotpotato/baked", "coldpotato/baked") => 0; lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY; @@ -536,7 +604,7 @@ code = ''' lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "hotpotato") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -558,7 +626,7 @@ code = ''' lfs_unmount(&lfs) => 0; // final remove - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY; lfs_remove(&lfs, "hotpotato/baked") => 0; lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY; @@ -568,7 +636,7 @@ code = ''' lfs_remove(&lfs, "hotpotato") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -584,17 +652,22 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # recursive remove -define.N = [10, 100] +[cases.recursive_remove] +defines.N = [10, 100] +if = 'N < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + 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); @@ -602,6 +675,7 @@ code = ''' 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); @@ -611,7 +685,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; lfs_dir_open(&lfs, &dir, "prickly-pear") => 0; @@ -622,6 +696,7 @@ code = ''' 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); @@ -636,22 +711,24 @@ code = ''' lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT; lfs_unmount(&lfs) => 0; ''' -[[case]] # other error cases +[cases.other_errors] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "potato") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "burito", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST; lfs_mkdir(&lfs, "burito") => LFS_ERR_EXIST; @@ -659,6 +736,7 @@ code = ''' LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; lfs_file_open(&lfs, &file, "potato", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "tomato") => LFS_ERR_NOENT; lfs_dir_open(&lfs, &dir, "burito") => LFS_ERR_NOTDIR; lfs_file_open(&lfs, &file, "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT; @@ -678,6 +756,7 @@ code = ''' // check that errors did not corrupt directory lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); assert(strcmp(info.name, ".") == 0); @@ -696,7 +775,7 @@ code = ''' lfs_unmount(&lfs) => 0; // or on disk - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(info.type == LFS_TYPE_DIR); @@ -715,21 +794,26 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # directory seek -define.COUNT = [4, 128, 132] +[cases.directory_seek] +defines.COUNT = [4, 128, 132] +if = 'COUNT < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hello") => 0; for (int i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "hello/kitty%03d", i); lfs_mkdir(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; for (int j = 2; j < COUNT; j++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "hello") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -739,6 +823,7 @@ code = ''' lfs_soff_t pos; for (int i = 0; i < j; i++) { + char path[1024]; sprintf(path, "kitty%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); @@ -748,13 +833,14 @@ code = ''' } lfs_dir_seek(&lfs, &dir, pos) => 0; + char path[1024]; sprintf(path, "kitty%03d", j); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); assert(info.type == LFS_TYPE_DIR); lfs_dir_rewind(&lfs, &dir) => 0; - sprintf(path, "kitty%03d", 0); + sprintf(path, "kitty%03u", 0); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -776,20 +862,25 @@ code = ''' } ''' -[[case]] # root seek -define.COUNT = [4, 128, 132] +[cases.root_seek] +defines.COUNT = [4, 128, 132] +if = 'COUNT < BLOCK_COUNT/2' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "hi%03d", i); lfs_mkdir(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; for (int j = 2; j < COUNT; j++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -799,6 +890,7 @@ code = ''' lfs_soff_t pos; for (int i = 0; i < j; i++) { + char path[1024]; sprintf(path, "hi%03d", i); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); @@ -808,13 +900,14 @@ code = ''' } lfs_dir_seek(&lfs, &dir, pos) => 0; + char path[1024]; sprintf(path, "hi%03d", j); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); assert(info.type == LFS_TYPE_DIR); lfs_dir_rewind(&lfs, &dir) => 0; - sprintf(path, "hi%03d", 0); + sprintf(path, "hi%03u", 0); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); diff --git a/tests/test_entries.toml b/tests/test_entries.toml index 81e175f..6c1f1d7 100644 --- a/tests/test_entries.toml +++ b/tests/test_entries.toml @@ -2,19 +2,23 @@ # Note that these tests are intended for 512 byte inline sizes. They should # still pass with other inline sizes but wouldn't be testing anything. -define.LFS_CACHE_SIZE = 512 -if = 'LFS_CACHE_SIZE % LFS_PROG_SIZE == 0 && LFS_CACHE_SIZE == 512' +defines.CACHE_SIZE = 512 +if = 'CACHE_SIZE % PROG_SIZE == 0 && CACHE_SIZE == 512' -[[case]] # entry grow test +[cases.entry_grow] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 20 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 20; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -94,16 +98,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # entry shrink test +[cases.entry_shrink] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 20 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 20; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -183,16 +191,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # entry spill test +[cases.entry_spill] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 200 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 200; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -256,16 +268,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # entry push spill test +[cases.entry_push_spill] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 200 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 200; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -345,16 +361,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # entry push spill two test +[cases.entry_push_spill_two] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 200 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 200; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -449,16 +469,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # entry drop test +[cases.entry_drop] code = ''' uint8_t wbuffer[1024]; uint8_t rbuffer[1024]; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // write hi0 200 + char path[1024]; + lfs_size_t size; sprintf(path, "hi0"); size = 200; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; memset(wbuffer, 'c', size); @@ -491,6 +515,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_remove(&lfs, "hi1") => 0; + struct lfs_info info; lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT; // read hi0 200 sprintf(path, "hi0"); size = 200; @@ -547,15 +572,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # create too big +[cases.create_too_big] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + char path[1024]; memset(path, 'm', 200); path[200] = '\0'; - size = 400; + lfs_size_t size = 400; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; uint8_t wbuffer[1024]; @@ -572,15 +600,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # resize too big +[cases.resize_too_big] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + char path[1024]; memset(path, 'm', 200); path[200] = '\0'; - size = 40; + lfs_size_t size = 40; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; uint8_t wbuffer[1024]; diff --git a/tests/test_evil.toml b/tests/test_evil.toml index 920d3a0..78a5034 100644 --- a/tests/test_evil.toml +++ b/tests/test_evil.toml @@ -3,16 +3,17 @@ # invalid pointer tests (outside of block_count) -[[case]] # invalid tail-pointer test -define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] -define.INVALSET = [0x3, 0x1, 0x2] +[cases.invalid_tail_pointer] +defines.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] +defines.INVALSET = [0x3, 0x1, 0x2] in = "lfs.c" code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // change tail-pointer to invalid pointers - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( @@ -23,25 +24,27 @@ code = ''' lfs_deinit(&lfs) => 0; // test that mount fails gracefully - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' -[[case]] # invalid dir pointer test -define.INVALSET = [0x3, 0x1, 0x2] +[cases.invalid_dir_pointer] +defines.INVALSET = [0x3, 0x1, 0x2] in = "lfs.c" code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // make a dir - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "dir_here") => 0; lfs_unmount(&lfs) => 0; // change the dir pointer to be invalid - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; // make sure id 1 == our directory + uint8_t buffer[1024]; lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer) @@ -57,14 +60,17 @@ code = ''' // test that accessing our bad dir fails, note there's a number // of ways to access the dir, some can fail, but some don't - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "dir_here", &info) => 0; assert(strcmp(info.name, "dir_here") == 0); assert(info.type == LFS_TYPE_DIR); + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT; lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT; lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT; + lfs_file_t file; lfs_file_open(&lfs, &file, "dir_here/file_here", LFS_O_RDONLY) => LFS_ERR_CORRUPT; lfs_file_open(&lfs, &file, "dir_here/file_here", @@ -72,24 +78,27 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # invalid file pointer test +[cases.invalid_file_pointer] in = "lfs.c" -define.SIZE = [10, 1000, 100000] # faked file size +defines.SIZE = [10, 1000, 100000] # faked file size code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // make a file - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "file_here", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; // change the file pointer to be invalid - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; // make sure id 1 == our file + uint8_t buffer[1024]; lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) @@ -103,7 +112,8 @@ code = ''' // test that accessing our bad file fails, note there's a number // of ways to access the dir, some can fail, but some don't - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "file_here", &info) => 0; assert(strcmp(info.name, "file_here") == 0); assert(info.type == LFS_TYPE_REG); @@ -114,20 +124,22 @@ code = ''' lfs_file_close(&lfs, &file) => 0; // any allocs that traverse CTZ must unfortunately must fail - if (SIZE > 2*LFS_BLOCK_SIZE) { + if (SIZE > 2*BLOCK_SIZE) { lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; } lfs_unmount(&lfs) => 0; ''' -[[case]] # invalid pointer in CTZ skip-list test -define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE'] +[cases.invalid_ctz_pointer] # invalid pointer in CTZ skip-list test +defines.SIZE = ['2*BLOCK_SIZE', '3*BLOCK_SIZE', '4*BLOCK_SIZE'] in = "lfs.c" code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // make a file - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "file_here", LFS_O_WRONLY | LFS_O_CREAT) => 0; for (int i = 0; i < SIZE; i++) { @@ -137,10 +149,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; // change pointer in CTZ skip-list to be invalid - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; // make sure id 1 == our file and get our CTZ structure + uint8_t buffer[4*BLOCK_SIZE]; lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x700, 0x3ff, 0), LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) @@ -153,18 +166,19 @@ code = ''' => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)); lfs_ctz_fromle32(&ctz); // rewrite block to contain bad pointer - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; + uint8_t bbuffer[BLOCK_SIZE]; + cfg->read(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0; uint32_t bad = lfs_tole32(0xcccccccc); memcpy(&bbuffer[0], &bad, sizeof(bad)); memcpy(&bbuffer[4], &bad, sizeof(bad)); - cfg.erase(&cfg, ctz.head) => 0; - cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; + cfg->erase(cfg, ctz.head) => 0; + cfg->prog(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0; lfs_deinit(&lfs) => 0; // test that accessing our bad file fails, note there's a number // of ways to access the dir, some can fail, but some don't - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "file_here", &info) => 0; assert(strcmp(info.name, "file_here") == 0); assert(info.type == LFS_TYPE_REG); @@ -175,22 +189,23 @@ code = ''' lfs_file_close(&lfs, &file) => 0; // any allocs that traverse CTZ must unfortunately must fail - if (SIZE > 2*LFS_BLOCK_SIZE) { + if (SIZE > 2*BLOCK_SIZE) { lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; } lfs_unmount(&lfs) => 0; ''' -[[case]] # invalid gstate pointer -define.INVALSET = [0x3, 0x1, 0x2] +[cases.invalid_gstate_pointer] +defines.INVALSET = [0x3, 0x1, 0x2] in = "lfs.c" code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // create an invalid gstate - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ @@ -202,21 +217,22 @@ code = ''' // test that mount fails gracefully // mount may not fail, but our first alloc should fail when // we try to fix the gstate - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT; lfs_unmount(&lfs) => 0; ''' # cycle detection/recovery tests -[[case]] # metadata-pair threaded-list loop test +[cases.mdir_loop] # metadata-pair threaded-list loop test in = "lfs.c" code = ''' // create littlefs - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // change tail-pointer to point to ourself - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( @@ -225,20 +241,21 @@ code = ''' lfs_deinit(&lfs) => 0; // test that mount fails gracefully - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' -[[case]] # metadata-pair threaded-list 2-length loop test +[cases.mdir_loop_2] # metadata-pair threaded-list 2-length loop test in = "lfs.c" code = ''' // create littlefs with child dir - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "child") => 0; lfs_unmount(&lfs) => 0; // find child - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_block_t pair[2]; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; @@ -255,20 +272,21 @@ code = ''' lfs_deinit(&lfs) => 0; // test that mount fails gracefully - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' -[[case]] # metadata-pair threaded-list 1-length child loop test +[cases.mdir_loop_child] # metadata-pair threaded-list 1-length child loop test in = "lfs.c" code = ''' // create littlefs with child dir - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "child") => 0; lfs_unmount(&lfs) => 0; // find child - lfs_init(&lfs, &cfg) => 0; + lfs_init(&lfs, cfg) => 0; lfs_mdir_t mdir; lfs_block_t pair[2]; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; @@ -284,5 +302,5 @@ code = ''' lfs_deinit(&lfs) => 0; // test that mount fails gracefully - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' diff --git a/tests/test_exhaustion.toml b/tests/test_exhaustion.toml index 569611c..1914d62 100644 --- a/tests/test_exhaustion.toml +++ b/tests/test_exhaustion.toml @@ -1,30 +1,34 @@ -[[case]] # test running a filesystem to exhaustion -define.LFS_ERASE_CYCLES = 10 -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' -define.LFS_BADBLOCK_BEHAVIOR = [ +# test running a filesystem to exhaustion +[cases.exhaustion] +defines.ERASE_CYCLES = 10 +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2' +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', 'LFS_TESTBD_BADBLOCK_PROGNOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] -define.FILES = 10 +defines.FILES = 10 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "roadrunner") => 0; lfs_unmount(&lfs) => 0; uint32_t cycle = 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; @@ -33,14 +37,14 @@ code = ''' lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); lfs_unmount(&lfs) => 0; goto exhausted; } } - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { lfs_unmount(&lfs) => 0; @@ -50,10 +54,12 @@ code = ''' for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); @@ -71,10 +77,12 @@ code = ''' exhausted: // should still be readable - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "roadrunner/test%d", i); + struct lfs_info info; lfs_stat(&lfs, path, &info) => 0; } lfs_unmount(&lfs) => 0; @@ -82,31 +90,35 @@ exhausted: LFS_WARN("completed %d cycles", cycle); ''' -[[case]] # test running a filesystem to exhaustion - # which also requires expanding superblocks -define.LFS_ERASE_CYCLES = 10 -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' -define.LFS_BADBLOCK_BEHAVIOR = [ +# test running a filesystem to exhaustion +# which also requires expanding superblocks +[cases.exhaustion_superblocks] +defines.ERASE_CYCLES = 10 +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2' +defines.BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_TESTBD_BADBLOCK_ERASEERROR', 'LFS_TESTBD_BADBLOCK_READERROR', 'LFS_TESTBD_BADBLOCK_PROGNOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP', ] -define.FILES = 10 +defines.FILES = 10 code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; uint32_t cycle = 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size + char path[1024]; sprintf(path, "test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; @@ -115,14 +127,14 @@ code = ''' lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); lfs_unmount(&lfs) => 0; goto exhausted; } } - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { lfs_unmount(&lfs) => 0; @@ -132,10 +144,12 @@ code = ''' for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); @@ -153,9 +167,11 @@ code = ''' exhausted: // should still be readable - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; + struct lfs_info info; sprintf(path, "test%d", i); lfs_stat(&lfs, path, &info) => 0; } @@ -169,35 +185,39 @@ exhausted: # into increasing the block devices lifetime. This is something we can actually # check for. -[[case]] # wear-level test running a filesystem to exhaustion -define.LFS_ERASE_CYCLES = 20 -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' -define.FILES = 10 +# wear-level test running a filesystem to exhaustion +[cases.wear_leveling_exhaustion] +defines.ERASE_CYCLES = 20 +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2' +defines.FILES = 10 code = ''' uint32_t run_cycles[2]; - const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; + const uint32_t run_block_count[2] = {BLOCK_COUNT/2, BLOCK_COUNT}; for (int run = 0; run < 2; run++) { - for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { - lfs_testbd_setwear(&cfg, b, - (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; + for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) { + lfs_testbd_setwear(cfg, b, + (b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0; } - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "roadrunner") => 0; lfs_unmount(&lfs) => 0; uint32_t cycle = 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; @@ -206,14 +226,14 @@ code = ''' lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); lfs_unmount(&lfs) => 0; goto exhausted; } } - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { lfs_unmount(&lfs) => 0; @@ -223,10 +243,12 @@ code = ''' for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); @@ -244,9 +266,11 @@ code = ''' exhausted: // should still be readable - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; + struct lfs_info info; sprintf(path, "roadrunner/test%d", i); lfs_stat(&lfs, path, &info) => 0; } @@ -261,32 +285,36 @@ exhausted: LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); ''' -[[case]] # wear-level test + expanding superblock -define.LFS_ERASE_CYCLES = 20 -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' -define.FILES = 10 +# wear-level test + expanding superblock +[cases.wear_leveling_exhaustion_superblocks] +defines.ERASE_CYCLES = 20 +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.BLOCK_CYCLES = 'ERASE_CYCLES / 2' +defines.FILES = 10 code = ''' uint32_t run_cycles[2]; - const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; + const uint32_t run_block_count[2] = {BLOCK_COUNT/2, BLOCK_COUNT}; for (int run = 0; run < 2; run++) { - for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { - lfs_testbd_setwear(&cfg, b, - (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; + for (lfs_block_t b = 0; b < BLOCK_COUNT; b++) { + lfs_testbd_setwear(cfg, b, + (b < run_block_count[run]) ? 0 : ERASE_CYCLES) => 0; } - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; uint32_t cycle = 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size + char path[1024]; sprintf(path, "test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; @@ -295,14 +323,14 @@ code = ''' lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); lfs_unmount(&lfs) => 0; goto exhausted; } } - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { lfs_unmount(&lfs) => 0; @@ -312,10 +340,12 @@ code = ''' for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "test%d", i); srand(cycle * i); - size = 1 << ((rand() % 10)+2); + lfs_size_t size = 1 << ((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); @@ -333,9 +363,11 @@ code = ''' exhausted: // should still be readable - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; + struct lfs_info info; sprintf(path, "test%d", i); lfs_stat(&lfs, path, &info) => 0; } @@ -350,28 +382,32 @@ exhausted: LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); ''' -[[case]] # test that we wear blocks roughly evenly -define.LFS_ERASE_CYCLES = 0xffffffff -define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster -define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] -define.CYCLES = 100 -define.FILES = 10 -if = 'LFS_BLOCK_CYCLES < CYCLES/10' +# test that we wear blocks roughly evenly +[cases.wear_leveling_distribution] +defines.ERASE_CYCLES = 0xffffffff +defines.BLOCK_COUNT = 256 # small bd so test runs faster +defines.BLOCK_CYCLES = [5, 4, 3, 2, 1] +defines.CYCLES = 100 +defines.FILES = 10 +if = 'BLOCK_CYCLES < CYCLES/10' code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "roadrunner") => 0; lfs_unmount(&lfs) => 0; uint32_t cycle = 0; while (cycle < CYCLES) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << 4; //((rand() % 10)+2); + lfs_size_t size = 1 << 4; //((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; @@ -380,14 +416,14 @@ code = ''' lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); lfs_unmount(&lfs) => 0; goto exhausted; } } - err = lfs_file_close(&lfs, &file); + int err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { lfs_unmount(&lfs) => 0; @@ -397,10 +433,12 @@ code = ''' for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; sprintf(path, "roadrunner/test%d", i); srand(cycle * i); - size = 1 << 4; //((rand() % 10)+2); + lfs_size_t size = 1 << 4; //((rand() % 10)+2); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); @@ -418,9 +456,11 @@ code = ''' exhausted: // should still be readable - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors + char path[1024]; + struct lfs_info info; sprintf(path, "roadrunner/test%d", i); lfs_stat(&lfs, path, &info) => 0; } @@ -433,8 +473,8 @@ exhausted: lfs_testbd_wear_t totalwear = 0; lfs_testbd_wear_t maxwear = 0; // skip 0 and 1 as superblock movement is intentionally avoided - for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { - lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); + for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) { + lfs_testbd_wear_t wear = lfs_testbd_getwear(cfg, b); printf("%08x: wear %d\n", b, wear); assert(wear >= 0); if (wear < minwear) { @@ -445,15 +485,15 @@ exhausted: } totalwear += wear; } - lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT; + lfs_testbd_wear_t avgwear = totalwear / BLOCK_COUNT; LFS_WARN("max wear: %d cycles", maxwear); - LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT); + LFS_WARN("avg wear: %d cycles", totalwear / (int)BLOCK_COUNT); LFS_WARN("min wear: %d cycles", minwear); // find standard deviation^2 lfs_testbd_wear_t dev2 = 0; - for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { - lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); + for (lfs_block_t b = 2; b < BLOCK_COUNT; b++) { + lfs_testbd_wear_t wear = lfs_testbd_getwear(cfg, b); assert(wear >= 0); lfs_testbd_swear_t diff = wear - avgwear; dev2 += diff*diff; diff --git a/tests/test_files.toml b/tests/test_files.toml index 5463054..026e47f 100644 --- a/tests/test_files.toml +++ b/tests/test_files.toml @@ -1,17 +1,20 @@ -[[case]] # simple file test +[cases.simple_file] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + 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; - size = strlen("Hello World!")+1; + 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_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); @@ -19,17 +22,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # larger files -define.SIZE = [32, 8192, 262144, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 33, 1, 1023] +[cases.large_files] +defines.SIZE = [32, 8192, 262144, 0, 7, 8193] +defines.CHUNKSIZE = [31, 16, 33, 1, 1023] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // write - lfs_mount(&lfs, &cfg) => 0; + 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; srand(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++) { @@ -41,7 +47,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE; srand(1); @@ -57,15 +63,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # rewriting files -define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 1] +[cases.rewriting_files] +defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] +defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] +defines.CHUNKSIZE = [31, 16, 1] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // write - lfs_mount(&lfs, &cfg) => 1; + 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; srand(1); @@ -80,7 +89,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE1; srand(1); @@ -96,7 +105,7 @@ code = ''' lfs_unmount(&lfs) => 0; // rewrite - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0; srand(2); for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { @@ -110,7 +119,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2); srand(2); @@ -139,15 +148,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # appending files -define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 1] +[cases.appending_files] +defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] +defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] +defines.CHUNKSIZE = [31, 16, 1] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // write - lfs_mount(&lfs, &cfg) => 0; + 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; srand(1); @@ -162,7 +174,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE1; srand(1); @@ -178,7 +190,7 @@ code = ''' lfs_unmount(&lfs) => 0; // append - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0; srand(2); for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { @@ -192,7 +204,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE1 + SIZE2; srand(1); @@ -216,15 +228,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # truncating files -define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 1] +[cases.truncating_files] +defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] +defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] +defines.CHUNKSIZE = [31, 16, 1] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // write - lfs_mount(&lfs, &cfg) => 0; + 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; srand(1); @@ -239,7 +254,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE1; srand(1); @@ -255,7 +270,7 @@ code = ''' lfs_unmount(&lfs) => 0; // truncate - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0; srand(2); for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { @@ -269,7 +284,7 @@ code = ''' lfs_unmount(&lfs) => 0; // read - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => SIZE2; srand(2); @@ -285,22 +300,25 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant file writing -define.SIZE = [32, 0, 7, 2049] -define.CHUNKSIZE = [31, 16, 65] +[cases.reentrant_file_writing] +defines.SIZE = [32, 0, 7, 2049] +defines.CHUNKSIZE = [31, 16, 65] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + 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 - size = lfs_file_size(&lfs, &file); + lfs_size_t size = lfs_file_size(&lfs, &file); assert(size == 0 || size == SIZE); lfs_file_close(&lfs, &file) => 0; } @@ -333,8 +351,8 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant file writing with syncs -define = [ +[cases.reentrant_file_writing_sync] +defines = [ # append (O(n)) {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, # truncate (O(n^2)) @@ -344,17 +362,20 @@ define = [ ] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + 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 - size = lfs_file_size(&lfs, &file); + lfs_size_t size = lfs_file_size(&lfs, &file); assert(size <= SIZE); srand(1); for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) { @@ -370,7 +391,7 @@ code = ''' // write lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0; - size = lfs_file_size(&lfs, &file); + lfs_size_t size = lfs_file_size(&lfs, &file); assert(size <= SIZE); srand(1); lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0; @@ -403,19 +424,22 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many files -define.N = 300 +[cases.many_files] +defines.N = 300 code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // create N files of 7 bytes - lfs_mount(&lfs, &cfg) => 0; + 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]; - size = 7; - snprintf(wbuffer, size, "Hi %03d", i); + lfs_size_t size = 7; + sprintf(wbuffer, "Hi %03d", i); lfs_file_write(&lfs, &file, wbuffer, size) => size; lfs_file_close(&lfs, &file) => 0; @@ -428,25 +452,28 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many files with power cycle -define.N = 300 +[cases.many_files_power_cycle] +defines.N = 300 code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // create N files of 7 bytes - lfs_mount(&lfs, &cfg) => 0; + 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]; - size = 7; - snprintf(wbuffer, size, "Hi %03d", i); + 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_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); @@ -455,22 +482,25 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # many files with power loss -define.N = 300 +[cases.many_files_power_loss] +defines.N = 300 reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + 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]; - size = 7; - snprintf(wbuffer, size, "Hi %03d", i); + 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; } diff --git a/tests/test_interspersed.toml b/tests/test_interspersed.toml index 87a0578..92d96d8 100644 --- a/tests/test_interspersed.toml +++ b/tests/test_interspersed.toml @@ -1,13 +1,15 @@ -[[case]] # interspersed file test -define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +[cases.interspersed_files] +defines.SIZE = [10, 100] +defines.FILES = [4, 10, 26] code = ''' + lfs_t lfs; lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_file_open(&lfs, &files[j], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; @@ -23,7 +25,9 @@ code = ''' lfs_file_close(&lfs, &files[j]); } + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -31,6 +35,7 @@ code = ''' assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); @@ -41,12 +46,14 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0; } for (int i = 0; i < 10; i++) { for (int j = 0; j < FILES; j++) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &files[j], buffer, 1) => 1; assert(buffer[0] == alphas[j]); } @@ -59,15 +66,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # interspersed remove file test -define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +[cases.interspersed_remove_files] +defines.SIZE = [10, 100] +defines.FILES = [4, 10, 26] code = ''' + lfs_t lfs; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; for (int i = 0; i < SIZE; i++) { @@ -77,18 +87,22 @@ code = ''' } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "zzz", LFS_O_WRONLY | LFS_O_CREAT) => 0; for (int j = 0; j < FILES; j++) { lfs_file_write(&lfs, &file, (const void*)"~", 1) => 1; lfs_file_sync(&lfs, &file) => 0; + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_remove(&lfs, path) => 0; } lfs_file_close(&lfs, &file); + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -104,6 +118,7 @@ code = ''' lfs_file_open(&lfs, &file, "zzz", LFS_O_RDONLY) => 0; for (int i = 0; i < FILES; i++) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 1) => 1; assert(buffer[0] == '~'); } @@ -112,11 +127,12 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # remove inconveniently test -define.SIZE = [10, 100] +[cases.remove_inconveniently] +defines.SIZE = [10, 100] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_t files[3]; lfs_file_open(&lfs, &files[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &files[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0; @@ -140,7 +156,9 @@ code = ''' lfs_file_close(&lfs, &files[1]); lfs_file_close(&lfs, &files[2]); + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -161,6 +179,7 @@ code = ''' lfs_file_open(&lfs, &files[0], "e", LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &files[1], "g", LFS_O_RDONLY) => 0; for (int i = 0; i < SIZE; i++) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &files[0], buffer, 1) => 1; assert(buffer[0] == 'e'); lfs_file_read(&lfs, &files[1], buffer, 1) => 1; @@ -172,21 +191,23 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant interspersed file test -define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +[cases.reentrant_interspersed_files] +defines.SIZE = [10, 100] +defines.FILES = [4, 10, 26] reentrant = true code = ''' + lfs_t lfs; lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; - err = lfs_mount(&lfs, &cfg); + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_file_open(&lfs, &files[j], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; @@ -194,8 +215,8 @@ code = ''' for (int i = 0; i < SIZE; i++) { for (int j = 0; j < FILES; j++) { - size = lfs_file_size(&lfs, &files[j]); - assert((int)size >= 0); + lfs_ssize_t size = lfs_file_size(&lfs, &files[j]); + assert(size >= 0); if ((int)size <= i) { lfs_file_write(&lfs, &files[j], &alphas[j], 1) => 1; lfs_file_sync(&lfs, &files[j]) => 0; @@ -207,7 +228,9 @@ code = ''' lfs_file_close(&lfs, &files[j]); } + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS_TYPE_DIR); @@ -215,6 +238,7 @@ code = ''' assert(strcmp(info.name, "..") == 0); assert(info.type == LFS_TYPE_DIR); for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, path) == 0); @@ -225,12 +249,14 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; for (int j = 0; j < FILES; j++) { + char path[1024]; sprintf(path, "%c", alphas[j]); lfs_file_open(&lfs, &files[j], path, LFS_O_RDONLY) => 0; } for (int i = 0; i < 10; i++) { for (int j = 0; j < FILES; j++) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &files[j], buffer, 1) => 1; assert(buffer[0] == alphas[j]); } diff --git a/tests/test_move.toml b/tests/test_move.toml index bb3b713..f1825e4 100644 --- a/tests/test_move.toml +++ b/tests/test_move.toml @@ -1,11 +1,13 @@ -[[case]] # move file +[cases.move_file] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; lfs_mkdir(&lfs, "d") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; @@ -13,11 +15,13 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -44,6 +48,7 @@ code = ''' lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 5) => 5; memcmp(buffer, "hola\n", 5) => 0; lfs_file_read(&lfs, &file, buffer, 8) => 8; @@ -55,31 +60,35 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # noop move, yes this is legal +[cases.nop_move] # yes this is legal code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "hi") => 0; lfs_rename(&lfs, "hi", "hi") => 0; lfs_mkdir(&lfs, "hi/hi") => 0; lfs_rename(&lfs, "hi/hi", "hi/hi") => 0; lfs_mkdir(&lfs, "hi/hi/hi") => 0; lfs_rename(&lfs, "hi/hi/hi", "hi/hi/hi") => 0; + struct lfs_info info; lfs_stat(&lfs, "hi/hi/hi", &info) => 0; assert(strcmp(info.name, "hi") == 0); assert(info.type == LFS_TYPE_DIR); lfs_unmount(&lfs) => 0; ''' -[[case]] # move file corrupt source +[cases.move_file_corrupt_source] in = "lfs.c" code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; lfs_mkdir(&lfs, "d") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; @@ -87,28 +96,30 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -146,16 +157,19 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move file corrupt source and dest +# move file corrupt source and dest +[cases.move_file_corrupt_source_dest] in = "lfs.c" -if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit +if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; lfs_mkdir(&lfs, "d") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; @@ -163,44 +177,46 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // corrupt the destination - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "c") => 0; block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -238,16 +254,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move file after corrupt +[cases.move_file_after_corrupt] in = "lfs.c" -if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit +if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; lfs_mkdir(&lfs, "d") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; @@ -255,49 +273,51 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // corrupt the destination - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "c") => 0; block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // continue move - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -335,13 +355,14 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # simple reentrant move file +[cases.reentrant_move_file] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } err = lfs_mkdir(&lfs, "a"); assert(!err || err == LFS_ERR_EXIST); @@ -354,9 +375,10 @@ code = ''' lfs_unmount(&lfs) => 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // there should never exist _2_ hello files int count = 0; + struct lfs_info info; if (lfs_stat(&lfs, "a/hello", &info) == 0) { assert(strcmp(info.name, "hello") == 0); assert(info.type == LFS_TYPE_REG); @@ -384,7 +406,7 @@ code = ''' assert(count <= 1); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; if (lfs_stat(&lfs, "a/hello", &info) == 0 && info.size > 0) { lfs_rename(&lfs, "a/hello", "b/hello") => 0; } else if (lfs_stat(&lfs, "b/hello", &info) == 0) { @@ -397,6 +419,7 @@ code = ''' break; } else { // create file + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; @@ -407,7 +430,9 @@ code = ''' lfs_unmount(&lfs) => 0; } - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -431,10 +456,12 @@ code = ''' lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 5) => 5; memcmp(buffer, "hola\n", 5) => 0; lfs_file_read(&lfs, &file, buffer, 8) => 8; @@ -445,10 +472,11 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move dir +[cases.move_dir] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; @@ -459,11 +487,13 @@ code = ''' lfs_mkdir(&lfs, "a/hi/ohayo") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hi", "c/hi") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -510,11 +540,12 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move dir corrupt source +[cases.move_dir_corrupt_source] in = "lfs.c" code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; @@ -525,28 +556,30 @@ code = ''' lfs_mkdir(&lfs, "a/hi/ohayo") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hi", "c/hi") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -593,12 +626,13 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move dir corrupt source and dest +[cases.move_dir_corrupt_source_dest] in = "lfs.c" -if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit +if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; @@ -609,44 +643,46 @@ code = ''' lfs_mkdir(&lfs, "a/hi/ohayo") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hi", "c/hi") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // corrupt the destination - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "c") => 0; block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -693,12 +729,13 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move dir after corrupt +[cases.move_dir_after_corrupt] in = "lfs.c" -if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit +if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; @@ -709,49 +746,51 @@ code = ''' lfs_mkdir(&lfs, "a/hi/ohayo") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hi", "c/hi") => 0; lfs_unmount(&lfs) => 0; // corrupt the source - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // corrupt the destination - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "c") => 0; block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; // continue move - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hi", "c/hi") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -798,13 +837,14 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # simple reentrant move dir +[cases.reentrant_move_dir] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } err = lfs_mkdir(&lfs, "a"); assert(!err || err == LFS_ERR_EXIST); @@ -817,9 +857,10 @@ code = ''' lfs_unmount(&lfs) => 0; while (true) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // there should never exist _2_ hi directories int count = 0; + struct lfs_info info; if (lfs_stat(&lfs, "a/hi", &info) == 0) { assert(strcmp(info.name, "hi") == 0); assert(info.type == LFS_TYPE_DIR); @@ -843,7 +884,7 @@ code = ''' assert(count <= 1); lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; if (lfs_stat(&lfs, "a/hi", &info) == 0) { lfs_rename(&lfs, "a/hi", "b/hi") => 0; } else if (lfs_stat(&lfs, "b/hi", &info) == 0) { @@ -868,7 +909,9 @@ code = ''' lfs_unmount(&lfs) => 0; } - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -915,14 +958,16 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move state stealing +[cases.move_state_stealing] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "a") => 0; lfs_mkdir(&lfs, "b") => 0; lfs_mkdir(&lfs, "c") => 0; lfs_mkdir(&lfs, "d") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_write(&lfs, &file, "hola\n", 5) => 5; lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; @@ -930,21 +975,22 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "a/hello", "b/hello") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "b/hello", "c/hello") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_rename(&lfs, "c/hello", "d/hello") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 5) => 5; memcmp(buffer, "hola\n", 5) => 0; lfs_file_read(&lfs, &file, buffer, 8) => 8; @@ -954,12 +1000,13 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_remove(&lfs, "b") => 0; lfs_remove(&lfs, "c") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "a", &info) => 0; lfs_stat(&lfs, "b", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "c", &info) => LFS_ERR_NOENT; @@ -979,12 +1026,16 @@ code = ''' ''' # Other specific corner cases -[[case]] # create + delete in same commit with neighbors + +# create + delete in same commit with neighbors +[cases.create_delete_same] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // littlefs keeps files sorted, so we know the order these will be in + lfs_file_t file; lfs_file_open(&lfs, &file, "/1.move_me", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_close(&lfs, &file) => 0; @@ -1024,6 +1075,8 @@ code = ''' lfs_file_close(&lfs, &files[2]) => 0; // check that nothing was corrupted + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -1051,6 +1104,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 7) => 7; assert(strcmp((char*)buffer, "test.4") == 0); lfs_file_close(&lfs, &file) => 0; @@ -1124,13 +1178,15 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# Other specific corner cases -[[case]] # create + delete + delete in same commit with neighbors +# create + delete + delete in same commit with neighbors +[cases.create_delete_delete_same] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // littlefs keeps files sorted, so we know the order these will be in + lfs_file_t file; lfs_file_open(&lfs, &file, "/1.move_me", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_close(&lfs, &file) => 0; @@ -1175,6 +1231,8 @@ code = ''' lfs_file_close(&lfs, &files[2]) => 0; // check that nothing was corrupted + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -1202,6 +1260,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 7) => 7; assert(strcmp((char*)buffer, "test.4") == 0); lfs_file_close(&lfs, &file) => 0; @@ -1281,14 +1340,17 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # create + delete in different dirs with neighbors +# create + delete in different dirs with neighbors +[cases.create_delete_different] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // littlefs keeps files sorted, so we know the order these will be in lfs_mkdir(&lfs, "/dir.1") => 0; lfs_mkdir(&lfs, "/dir.2") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "/dir.1/1.move_me", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_close(&lfs, &file) => 0; @@ -1340,6 +1402,8 @@ code = ''' lfs_file_close(&lfs, &files[3]) => 0; // check that nothing was corrupted + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "/") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -1397,6 +1461,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_file_open(&lfs, &file, "/dir.1/0.before", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 7) => 7; assert(strcmp((char*)buffer, "test.5") == 0); lfs_file_close(&lfs, &file) => 0; @@ -1518,17 +1583,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move fix in relocation +# move fix in relocation +[cases.move_fix_relocation] in = "lfs.c" -define.RELOCATIONS = 'range(0x3+1)' -define.LFS_ERASE_CYCLES = 0xffffffff +defines.RELOCATIONS = [0x0, 0x1, 0x2, 0x3] +defines.ERASE_CYCLES = 0xffffffff code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "/parent") => 0; lfs_mkdir(&lfs, "/parent/child") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "/parent/1.move_me", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "move me", @@ -1568,15 +1636,17 @@ code = ''' // force specific directories to relocate if (RELOCATIONS & 0x1) { + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/parent"); - lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_dir_close(&lfs, &dir) => 0; } if (RELOCATIONS & 0x2) { + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/parent/child"); - lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_dir_close(&lfs, &dir) => 0; } @@ -1593,6 +1663,8 @@ code = ''' lfs_file_close(&lfs, &files[3]) => 0; // check that nothing was corrupted + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "/parent") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -1637,6 +1709,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_file_open(&lfs, &file, "/parent/0.before", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 7) => 7; assert(strcmp((char*)buffer, "test.5") == 0); lfs_file_close(&lfs, &file) => 0; @@ -1655,18 +1728,21 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # move fix in relocation with predecessor +# move fix in relocation with predecessor +[cases.move_fix_relocation_predecessor] in = "lfs.c" -define.RELOCATIONS = 'range(0x7+1)' -define.LFS_ERASE_CYCLES = 0xffffffff +defines.RELOCATIONS = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7] +defines.ERASE_CYCLES = 0xffffffff code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "/parent") => 0; lfs_mkdir(&lfs, "/parent/child") => 0; lfs_mkdir(&lfs, "/parent/sibling") => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "/parent/sibling/1.move_me", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_write(&lfs, &file, "move me", @@ -1706,21 +1782,24 @@ code = ''' // force specific directories to relocate if (RELOCATIONS & 0x1) { + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/parent"); - lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_dir_close(&lfs, &dir) => 0; } if (RELOCATIONS & 0x2) { + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/parent/sibling"); - lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_dir_close(&lfs, &dir) => 0; } if (RELOCATIONS & 0x4) { + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/parent/child"); - lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; - lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0; + lfs_testbd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_dir_close(&lfs, &dir) => 0; } @@ -1739,6 +1818,8 @@ code = ''' lfs_file_close(&lfs, &files[3]) => 0; // check that nothing was corrupted + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "/parent") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; assert(strcmp(info.name, ".") == 0); @@ -1796,6 +1877,7 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_file_open(&lfs, &file, "/parent/sibling/0.before", LFS_O_RDONLY) => 0; + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 7) => 7; assert(strcmp((char*)buffer, "test.5") == 0); lfs_file_close(&lfs, &file) => 0; diff --git a/tests/test_orphans.toml b/tests/test_orphans.toml index 241e273..fd9b521 100644 --- a/tests/test_orphans.toml +++ b/tests/test_orphans.toml @@ -1,9 +1,10 @@ -[[case]] # orphan test +[cases.orphan] in = "lfs.c" -if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit +if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "parent") => 0; lfs_mkdir(&lfs, "parent/orphan") => 0; lfs_mkdir(&lfs, "parent/child") => 0; @@ -13,29 +14,31 @@ code = ''' // corrupt the child's most recent commit, this should be the update // to the linked-list entry, which should orphan the orphan. Note this // makes a lot of assumptions about the remove operation. - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "parent/child") => 0; lfs_block_t block = dir.m.pair[0]; lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; - uint8_t bbuffer[LFS_BLOCK_SIZE]; - cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - int off = LFS_BLOCK_SIZE-1; - while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { + uint8_t buffer[BLOCK_SIZE]; + cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + int off = BLOCK_SIZE-1; + while (off >= 0 && buffer[off] == ERASE_VALUE) { off -= 1; } - memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); - cfg.erase(&cfg, block) => 0; - cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; - cfg.sync(&cfg) => 0; + memset(&buffer[off-3], BLOCK_SIZE, 3); + cfg->erase(cfg, block) => 0; + cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0; + cfg->sync(cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "parent/child", &info) => 0; lfs_fs_size(&lfs) => 8; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "parent/child", &info) => 0; lfs_fs_size(&lfs) => 8; @@ -48,7 +51,7 @@ code = ''' lfs_fs_size(&lfs) => 8; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "parent/child", &info) => 0; lfs_stat(&lfs, "parent/otherchild", &info) => 0; @@ -56,43 +59,48 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant testing for orphans, basically just spam mkdir/remove +# reentrant testing for orphans, basically just spam mkdir/remove +[cases.reentrant_orphan] reentrant = true # TODO fix this case, caused by non-DAG trees -if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' -define = [ +if = '!(DEPTH == 3 && CACHE_SIZE != 64)' +defines = [ {FILES=6, DEPTH=1, CYCLES=20}, {FILES=26, DEPTH=1, CYCLES=20}, {FILES=3, DEPTH=3, CYCLES=20}, ] code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } srand(1); const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; - for (int i = 0; i < CYCLES; i++) { + for (unsigned i = 0; i < CYCLES; i++) { // create random path char full_path[256]; - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); } // if it does not exist, we create it, else we destroy + struct lfs_info info; int res = lfs_stat(&lfs, full_path, &info); if (res == LFS_ERR_NOENT) { // create each directory in turn, ignore if dir already exists - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_mkdir(&lfs, path); assert(!err || err == LFS_ERR_EXIST); } - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; lfs_stat(&lfs, path, &info) => 0; @@ -106,6 +114,7 @@ code = ''' // try to delete path in reverse order, ignore if dir is not empty for (int d = DEPTH-1; d >= 0; d--) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_remove(&lfs, path); diff --git a/tests/test_paths.toml b/tests/test_paths.toml index a7474c0..310364d 100644 --- a/tests/test_paths.toml +++ b/tests/test_paths.toml @@ -1,13 +1,16 @@ -[[case]] # simple path test +# simple path test +[cases.path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; lfs_mkdir(&lfs, "tea/coldtea") => 0; + struct lfs_info info; lfs_stat(&lfs, "tea/hottea", &info) => 0; assert(strcmp(info.name, "hottea") == 0); lfs_stat(&lfs, "/tea/hottea", &info) => 0; @@ -21,15 +24,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # redundant slashes +# redundant slashes +[cases.redundant_slashes] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; lfs_mkdir(&lfs, "tea/coldtea") => 0; + struct lfs_info info; lfs_stat(&lfs, "/tea/hottea", &info) => 0; assert(strcmp(info.name, "hottea") == 0); lfs_stat(&lfs, "//tea//hottea", &info) => 0; @@ -45,15 +51,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # dot path test +# dot path test +[cases.dot_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; lfs_mkdir(&lfs, "tea/coldtea") => 0; + struct lfs_info info; lfs_stat(&lfs, "./tea/hottea", &info) => 0; assert(strcmp(info.name, "hottea") == 0); lfs_stat(&lfs, "/./tea/hottea", &info) => 0; @@ -71,10 +80,12 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # dot dot path test +# dot dot path test +[cases.dot_dot_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; @@ -84,6 +95,7 @@ code = ''' lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; + struct lfs_info info; lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0; assert(strcmp(info.name, "hottea") == 0); lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0; @@ -101,15 +113,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # trailing dot path test +# trailing dot path test +[cases.trailing_dot_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; lfs_mkdir(&lfs, "tea/coldtea") => 0; + struct lfs_info info; lfs_stat(&lfs, "tea/hottea/", &info) => 0; assert(strcmp(info.name, "hottea") == 0); lfs_stat(&lfs, "tea/hottea/.", &info) => 0; @@ -123,11 +138,14 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # leading dot path test +# leading dot path test +[cases.leading_dot_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, ".milk") => 0; + struct lfs_info info; lfs_stat(&lfs, ".milk", &info) => 0; strcmp(info.name, ".milk") => 0; lfs_stat(&lfs, "tea/.././.milk", &info) => 0; @@ -135,10 +153,12 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # root dot dot path test +# root dot dot path test +[cases.root_dot_dot_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "tea") => 0; lfs_mkdir(&lfs, "tea/hottea") => 0; lfs_mkdir(&lfs, "tea/warmtea") => 0; @@ -148,6 +168,7 @@ code = ''' lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; + struct lfs_info info; lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0; strcmp(info.name, "hottea") => 0; @@ -159,10 +180,13 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # invalid path tests +# invalid path tests +[cases.invalid_path] code = ''' - lfs_format(&lfs, &cfg); - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg); + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "dirt", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "dirt/ground", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "dirt/ground/earth", &info) => LFS_ERR_NOENT; @@ -172,6 +196,7 @@ code = ''' lfs_remove(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT; lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT; + lfs_file_t file; lfs_file_open(&lfs, &file, "dirt/ground", LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NOENT; lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT; @@ -180,15 +205,19 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # root operations +# root operations +[cases.root] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "/", &info) => 0; assert(strcmp(info.name, "/") == 0); assert(info.type == LFS_TYPE_DIR); lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST; + lfs_file_t file; lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR; @@ -196,10 +225,13 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # root representations +# root representations +[cases.root_reprs] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "/", &info) => 0; assert(strcmp(info.name, "/") == 0); assert(info.type == LFS_TYPE_DIR); @@ -221,10 +253,13 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # superblock conflict test +# superblock conflict test +[cases.superblock_conflict] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "littlefs", &info) => LFS_ERR_NOENT; lfs_remove(&lfs, "littlefs") => LFS_ERR_NOENT; @@ -237,18 +272,22 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # max path test +# max path test +[cases.max_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "coffee") => 0; lfs_mkdir(&lfs, "coffee/hotcoffee") => 0; lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; + char path[1024]; memset(path, 'w', LFS_NAME_MAX+1); path[LFS_NAME_MAX+1] = '\0'; lfs_mkdir(&lfs, path) => LFS_ERR_NAMETOOLONG; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG; @@ -261,19 +300,23 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # really big path test +# really big path test +[cases.really_big_path] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_mkdir(&lfs, "coffee") => 0; lfs_mkdir(&lfs, "coffee/hotcoffee") => 0; lfs_mkdir(&lfs, "coffee/warmcoffee") => 0; lfs_mkdir(&lfs, "coffee/coldcoffee") => 0; + char path[1024]; memset(path, 'w', LFS_NAME_MAX); path[LFS_NAME_MAX] = '\0'; lfs_mkdir(&lfs, path) => 0; lfs_remove(&lfs, path) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_close(&lfs, &file) => 0; diff --git a/tests/test_relocations.toml b/tests/test_relocations.toml index 71b1047..f177c73 100644 --- a/tests/test_relocations.toml +++ b/tests/test_relocations.toml @@ -1,15 +1,18 @@ # specific corner cases worth explicitly testing for -[[case]] # dangling split dir test -define.ITERATIONS = 20 -define.COUNT = 10 -define.LFS_BLOCK_CYCLES = [8, 1] +[cases.dangling_split_dir] +defines.ITERATIONS = 20 +defines.COUNT = 10 +defines.BLOCK_CYCLES = [8, 1] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // fill up filesystem so only ~16 blocks are left - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; + uint8_t buffer[512]; memset(buffer, 0, 512); - while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { + while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { lfs_file_write(&lfs, &file, buffer, 512) => 512; } lfs_file_close(&lfs, &file) => 0; @@ -17,18 +20,22 @@ code = ''' lfs_mkdir(&lfs, "child") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; - for (int j = 0; j < ITERATIONS; j++) { - for (int i = 0; i < COUNT; i++) { + lfs_mount(&lfs, cfg) => 0; + for (unsigned j = 0; j < ITERATIONS; j++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_close(&lfs, &file) => 0; } + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "child") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; lfs_dir_read(&lfs, &dir, &info) => 1; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "test%03d_loooooooooooooooooong_name", i); lfs_dir_read(&lfs, &dir, &info) => 1; strcmp(info.name, path) => 0; @@ -36,46 +43,54 @@ code = ''' lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; - if (j == ITERATIONS-1) { + if (j == (unsigned)ITERATIONS-1) { break; } - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); lfs_remove(&lfs, path) => 0; } } lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "child") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; lfs_dir_read(&lfs, &dir, &info) => 1; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "test%03d_loooooooooooooooooong_name", i); lfs_dir_read(&lfs, &dir, &info) => 1; strcmp(info.name, path) => 0; } lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); lfs_remove(&lfs, path) => 0; } lfs_unmount(&lfs) => 0; ''' -[[case]] # outdated head test -define.ITERATIONS = 20 -define.COUNT = 10 -define.LFS_BLOCK_CYCLES = [8, 1] +[cases.outdated_head] +defines.ITERATIONS = 20 +defines.COUNT = 10 +defines.BLOCK_CYCLES = [8, 1] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; // fill up filesystem so only ~16 blocks are left - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; + uint8_t buffer[512]; memset(buffer, 0, 512); - while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { + while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { lfs_file_write(&lfs, &file, buffer, 512) => 512; } lfs_file_close(&lfs, &file) => 0; @@ -83,18 +98,22 @@ code = ''' lfs_mkdir(&lfs, "child") => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; - for (int j = 0; j < ITERATIONS; j++) { - for (int i = 0; i < COUNT; i++) { + lfs_mount(&lfs, cfg) => 0; + for (unsigned j = 0; j < ITERATIONS; j++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; lfs_file_close(&lfs, &file) => 0; } + lfs_dir_t dir; + struct lfs_info info; lfs_dir_open(&lfs, &dir, "child") => 0; lfs_dir_read(&lfs, &dir, &info) => 1; lfs_dir_read(&lfs, &dir, &info) => 1; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "test%03d_loooooooooooooooooong_name", i); lfs_dir_read(&lfs, &dir, &info) => 1; strcmp(info.name, path) => 0; @@ -110,7 +129,8 @@ code = ''' lfs_dir_rewind(&lfs, &dir) => 0; lfs_dir_read(&lfs, &dir, &info) => 1; lfs_dir_read(&lfs, &dir, &info) => 1; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "test%03d_loooooooooooooooooong_name", i); lfs_dir_read(&lfs, &dir, &info) => 1; strcmp(info.name, path) => 0; @@ -126,7 +146,8 @@ code = ''' lfs_dir_rewind(&lfs, &dir) => 0; lfs_dir_read(&lfs, &dir, &info) => 1; lfs_dir_read(&lfs, &dir, &info) => 1; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "test%03d_loooooooooooooooooong_name", i); lfs_dir_read(&lfs, &dir, &info) => 1; strcmp(info.name, path) => 0; @@ -135,7 +156,8 @@ code = ''' lfs_dir_read(&lfs, &dir, &info) => 0; lfs_dir_close(&lfs, &dir) => 0; - for (int i = 0; i < COUNT; i++) { + for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); lfs_remove(&lfs, path) => 0; } @@ -143,45 +165,50 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant testing for relocations, this is the same as the - # orphan testing, except here we also set block_cycles so that - # almost every tree operation needs a relocation +# reentrant testing for relocations, this is the same as the +# orphan testing, except here we also set block_cycles so that +# almost every tree operation needs a relocation +[cases.reentrant_relocations] reentrant = true # TODO fix this case, caused by non-DAG trees -if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' -define = [ - {FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, - {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, - {FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1}, +if = '!(DEPTH == 3 && CACHE_SIZE != 64)' +defines = [ + {FILES=6, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, + {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, + {FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1}, ] code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } srand(1); const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; - for (int i = 0; i < CYCLES; i++) { + for (unsigned i = 0; i < CYCLES; i++) { // create random path char full_path[256]; - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); } // if it does not exist, we create it, else we destroy + struct lfs_info info; int res = lfs_stat(&lfs, full_path, &info); if (res == LFS_ERR_NOENT) { // create each directory in turn, ignore if dir already exists - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_mkdir(&lfs, path); assert(!err || err == LFS_ERR_EXIST); } - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; lfs_stat(&lfs, path, &info) => 0; @@ -194,7 +221,8 @@ code = ''' assert(info.type == LFS_TYPE_DIR); // try to delete path in reverse order, ignore if dir is not empty - for (int d = DEPTH-1; d >= 0; d--) { + for (unsigned d = DEPTH-1; d+1 > 0; d--) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_remove(&lfs, path); @@ -207,44 +235,49 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant testing for relocations, but now with random renames! +# reentrant testing for relocations, but now with random renames! +[cases.reentrant_relocations_renames] reentrant = true # TODO fix this case, caused by non-DAG trees -if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' -define = [ - {FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, - {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, - {FILES=3, DEPTH=3, CYCLES=20, LFS_BLOCK_CYCLES=1}, +if = '!(DEPTH == 3 && CACHE_SIZE != 64)' +defines = [ + {FILES=6, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, + {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, + {FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1}, ] code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } srand(1); const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; - for (int i = 0; i < CYCLES; i++) { + for (unsigned i = 0; i < CYCLES; i++) { // create random path char full_path[256]; - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); } // if it does not exist, we create it, else we destroy + struct lfs_info info; int res = lfs_stat(&lfs, full_path, &info); assert(!res || res == LFS_ERR_NOENT); if (res == LFS_ERR_NOENT) { // create each directory in turn, ignore if dir already exists - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_mkdir(&lfs, path); assert(!err || err == LFS_ERR_EXIST); } - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; lfs_stat(&lfs, path, &info) => 0; @@ -257,7 +290,7 @@ code = ''' // create new random path char new_path[256]; - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { sprintf(&new_path[2*d], "/%c", alpha[rand() % FILES]); } @@ -266,7 +299,8 @@ code = ''' assert(!res || res == LFS_ERR_NOENT); if (res == LFS_ERR_NOENT) { // stop once some dir is renamed - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(&path[2*d], &full_path[2*d]); path[2*d+2] = '\0'; strcpy(&path[128+2*d], &new_path[2*d]); @@ -278,7 +312,8 @@ code = ''' } } - for (int d = 0; d < DEPTH; d++) { + for (unsigned d = 0; d < DEPTH; d++) { + char path[1024]; strcpy(path, new_path); path[2*d+2] = '\0'; lfs_stat(&lfs, path, &info) => 0; @@ -290,7 +325,8 @@ code = ''' } else { // try to delete path in reverse order, // ignore if dir is not empty - for (int d = DEPTH-1; d >= 0; d--) { + for (unsigned d = DEPTH-1; d+1 > 0; d--) { + char path[1024]; strcpy(path, full_path); path[2*d+2] = '\0'; err = lfs_remove(&lfs, path); diff --git a/tests/test_seek.toml b/tests/test_seek.toml index 79d7728..383c1ba 100644 --- a/tests/test_seek.toml +++ b/tests/test_seek.toml @@ -1,6 +1,7 @@ -[[case]] # simple file seek -define = [ +# simple file seek +[cases.seek] +defines = [ {COUNT=132, SKIP=4}, {COUNT=132, SKIP=128}, {COUNT=200, SKIP=10}, @@ -9,11 +10,14 @@ define = [ {COUNT=4, SKIP=2}, ] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen("kittycatcat"); + size_t size = strlen("kittycatcat"); + uint8_t buffer[1024]; memcpy(buffer, "kittycatcat", size); for (int j = 0; j < COUNT; j++) { lfs_file_write(&lfs, &file, buffer, size); @@ -21,7 +25,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY) => 0; lfs_soff_t pos = -1; @@ -68,8 +72,9 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # simple file seek and write -define = [ +# simple file seek and write +[cases.seek_write] +defines = [ {COUNT=132, SKIP=4}, {COUNT=132, SKIP=128}, {COUNT=200, SKIP=10}, @@ -78,11 +83,14 @@ define = [ {COUNT=4, SKIP=2}, ] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen("kittycatcat"); + size_t size = strlen("kittycatcat"); + uint8_t buffer[1024]; memcpy(buffer, "kittycatcat", size); for (int j = 0; j < COUNT; j++) { lfs_file_write(&lfs, &file, buffer, size); @@ -90,7 +98,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; lfs_soff_t pos = -1; @@ -129,15 +137,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # boundary seek and writes -define.COUNT = 132 -define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"' +# boundary seek and writes +[cases.boundary_seek_write] +defines.COUNT = 132 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen("kittycatcat"); + size_t size = strlen("kittycatcat"); + uint8_t buffer[1024]; memcpy(buffer, "kittycatcat", size); for (int j = 0; j < COUNT; j++) { lfs_file_write(&lfs, &file, buffer, size); @@ -145,11 +156,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; size = strlen("hedgehoghog"); - const lfs_soff_t offsets[] = OFFSETS; + const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019, 1441}; for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { lfs_soff_t off = offsets[i]; @@ -183,8 +194,9 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # out of bounds seek -define = [ +# out of bounds seek +[cases.out_of_bounds_seek] +defines = [ {COUNT=132, SKIP=4}, {COUNT=132, SKIP=128}, {COUNT=200, SKIP=10}, @@ -193,18 +205,21 @@ define = [ {COUNT=4, SKIP=3}, ] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "kitty", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; - size = strlen("kittycatcat"); + size_t size = strlen("kittycatcat"); + uint8_t buffer[1024]; memcpy(buffer, "kittycatcat", size); for (int j = 0; j < COUNT; j++) { lfs_file_write(&lfs, &file, buffer, size); } lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; size = strlen("kittycatcat"); @@ -238,16 +253,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # inline write and seek -define.SIZE = [2, 4, 128, 132] +# inline write and seek +[cases.inline_write_seek] +defines.SIZE = [2, 4, 128, 132] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "tinykitty", LFS_O_RDWR | LFS_O_CREAT) => 0; int j = 0; int k = 0; + uint8_t buffer[1024]; memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26); for (unsigned i = 0; i < SIZE; i++) { lfs_file_write(&lfs, &file, &buffer[j++ % 26], 1) => 1; @@ -305,16 +324,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # file seek and write with power-loss +# file seek and write with power-loss +[cases.reentrant_seek_write] # must be power-of-2 for quadratic probing to be exhaustive -define.COUNT = [4, 64, 128] +defines.COUNT = [4, 64, 128] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } + lfs_file_t file; + uint8_t buffer[1024]; err = lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY); assert(!err || err == LFS_ERR_NOENT); if (!err) { @@ -334,14 +357,14 @@ code = ''' if (lfs_file_size(&lfs, &file) == 0) { for (int j = 0; j < COUNT; j++) { strcpy((char*)buffer, "kittycatcat"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); lfs_file_write(&lfs, &file, buffer, size) => size; } } lfs_file_close(&lfs, &file) => 0; strcpy((char*)buffer, "doggodogdog"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => COUNT*size; diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 407c845..d511675 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -1,41 +1,53 @@ -[[case]] # simple formatting test +# simple formatting test +[cases.format] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; ''' -[[case]] # mount/unmount +# mount/unmount +[cases.mount] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant format +# reentrant format +[cases.reentrant_format] reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } lfs_unmount(&lfs) => 0; ''' -[[case]] # invalid mount +# invalid mount +[cases.invalid_mount] code = ''' - lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; + lfs_t lfs; + lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' -[[case]] # expanding superblock -define.LFS_BLOCK_CYCLES = [32, 33, 1] -define.N = [10, 100, 1000] +# expanding superblock +[cases.expanding_superblock] +defines.LFS_BLOCK_CYCLES = [32, 33, 1] +defines.N = [10, 100, 1000] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { + lfs_file_t file; lfs_file_open(&lfs, &file, "dummy", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; + struct lfs_info info; lfs_stat(&lfs, "dummy", &info) => 0; assert(strcmp(info.name, "dummy") == 0); assert(info.type == LFS_TYPE_REG); @@ -44,25 +56,30 @@ code = ''' lfs_unmount(&lfs) => 0; // one last check after power-cycle - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "dummy", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; + struct lfs_info info; lfs_stat(&lfs, "dummy", &info) => 0; assert(strcmp(info.name, "dummy") == 0); assert(info.type == LFS_TYPE_REG); lfs_unmount(&lfs) => 0; ''' -[[case]] # expanding superblock with power cycle -define.LFS_BLOCK_CYCLES = [32, 33, 1] -define.N = [10, 100, 1000] +# expanding superblock with power cycle +[cases.expanding_superblock_power_cycle] +defines.LFS_BLOCK_CYCLES = [32, 33, 1] +defines.N = [10, 100, 1000] code = ''' - lfs_format(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; for (int i = 0; i < N; i++) { - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; // remove lingering dummy? - err = lfs_stat(&lfs, "dummy", &info); + struct lfs_info info; + int err = lfs_stat(&lfs, "dummy", &info); assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); if (!err) { assert(strcmp(info.name, "dummy") == 0); @@ -70,6 +87,7 @@ code = ''' lfs_remove(&lfs, "dummy") => 0; } + lfs_file_t file; lfs_file_open(&lfs, &file, "dummy", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; @@ -80,26 +98,30 @@ code = ''' } // one last check after power-cycle - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "dummy", &info) => 0; assert(strcmp(info.name, "dummy") == 0); assert(info.type == LFS_TYPE_REG); lfs_unmount(&lfs) => 0; ''' -[[case]] # reentrant expanding superblock -define.LFS_BLOCK_CYCLES = [2, 1] -define.N = 24 +# reentrant expanding superblock +[cases.reentrant_expanding_superblock] +defines.LFS_BLOCK_CYCLES = [2, 1] +defines.N = 24 reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } for (int i = 0; i < N; i++) { // remove lingering dummy? + struct lfs_info info; err = lfs_stat(&lfs, "dummy", &info); assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); if (!err) { @@ -108,6 +130,7 @@ code = ''' lfs_remove(&lfs, "dummy") => 0; } + lfs_file_t file; lfs_file_open(&lfs, &file, "dummy", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfs_file_close(&lfs, &file) => 0; @@ -119,7 +142,8 @@ code = ''' lfs_unmount(&lfs) => 0; // one last check after power-cycle - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + struct lfs_info info; lfs_stat(&lfs, "dummy", &info) => 0; assert(strcmp(info.name, "dummy") == 0); assert(info.type == LFS_TYPE_REG); diff --git a/tests/test_truncate.toml b/tests/test_truncate.toml index 850d7aa..fc83ce3 100644 --- a/tests/test_truncate.toml +++ b/tests/test_truncate.toml @@ -1,14 +1,18 @@ -[[case]] # simple truncate -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 +# simple truncate +[cases.truncate] +defines.MEDIUMSIZE = [32, 2048] +defines.LARGESIZE = 8192 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "baldynoop", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < LARGESIZE; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -17,7 +21,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => LARGESIZE; @@ -27,7 +31,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => MEDIUMSIZE; @@ -42,17 +46,21 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # truncate and read -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 +# truncate and read +[cases.truncate_read] +defines.MEDIUMSIZE = [32, 2048] +defines.LARGESIZE = 8192 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "baldyread", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < LARGESIZE; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -61,7 +69,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => LARGESIZE; @@ -78,7 +86,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldyread", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => MEDIUMSIZE; @@ -93,14 +101,18 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # write, truncate, and read +# write, truncate, and read +[cases.write_truncate_read] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "sequence", LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; - size = lfs_min(lfs.cfg->cache_size, sizeof(buffer)/2); + uint8_t buffer[1024]; + size_t size = lfs_min(lfs.cfg->cache_size, sizeof(buffer)/2); lfs_size_t qsize = size / 4; uint8_t *wb = buffer; uint8_t *rb = buffer + size; @@ -145,17 +157,21 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # truncate and write -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 +# truncate and write +[cases.truncate_write] +defines.MEDIUMSIZE = [32, 2048] +defines.LARGESIZE = 8192 code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "baldywrite", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < LARGESIZE; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -164,7 +180,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => LARGESIZE; @@ -181,7 +197,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldywrite", LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => MEDIUMSIZE; @@ -196,26 +212,30 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # truncate write under powerloss -define.SMALLSIZE = [4, 512] -define.MEDIUMSIZE = [32, 1024] -define.LARGESIZE = 2048 +# truncate write under powerloss +[cases.reentrant_truncate_write] +defines.SMALLSIZE = [4, 512] +defines.MEDIUMSIZE = [32, 1024] +defines.LARGESIZE = 2048 reentrant = true code = ''' - err = lfs_mount(&lfs, &cfg); + lfs_t lfs; + int err = lfs_mount(&lfs, cfg); if (err) { - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; } + lfs_file_t file; err = lfs_file_open(&lfs, &file, "baldy", LFS_O_RDONLY); assert(!err || err == LFS_ERR_NOENT); if (!err) { - size = lfs_file_size(&lfs, &file); + size_t size = lfs_file_size(&lfs, &file); assert(size == 0 || - size == LARGESIZE || - size == MEDIUMSIZE || - size == SMALLSIZE); + size == (size_t)LARGESIZE || + size == (size_t)MEDIUMSIZE || + size == (size_t)SMALLSIZE); for (lfs_off_t j = 0; j < size; j += 4) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, 4) => 4; assert(memcmp(buffer, "hair", 4) == 0 || memcmp(buffer, "bald", 4) == 0 || @@ -227,8 +247,9 @@ code = ''' lfs_file_open(&lfs, &file, "baldy", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; lfs_file_size(&lfs, &file) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < LARGESIZE; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -262,12 +283,14 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # more aggressive general truncation tests -define.CONFIG = 'range(6)' -define.SMALLSIZE = 32 -define.MEDIUMSIZE = 2048 -define.LARGESIZE = 8192 +# more aggressive general truncation tests +[cases.aggressive_truncate] +defines.CONFIG = [0,1,2,3,4,5] +defines.SMALLSIZE = 32 +defines.MEDIUMSIZE = 2048 +defines.LARGESIZE = 8192 code = ''' + lfs_t lfs; #define COUNT 5 const struct { lfs_off_t startsizes[COUNT]; @@ -312,16 +335,19 @@ code = ''' const lfs_off_t *hotsizes = configs[CONFIG].hotsizes; const lfs_off_t *coldsizes = configs[CONFIG].coldsizes; - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "hairyhead%d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < startsizes[i]; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; } @@ -340,21 +366,25 @@ code = ''' lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "hairyhead%d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => hotsizes[i]; - size = strlen("hair"); + size_t size = strlen("hair"); lfs_off_t j = 0; for (; j < startsizes[i] && j < hotsizes[i]; j += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; memcmp(buffer, "hair", size) => 0; } for (; j < hotsizes[i]; j += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; memcmp(buffer, "\0\0\0\0", size) => 0; } @@ -367,22 +397,26 @@ code = ''' lfs_unmount(&lfs) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; for (unsigned i = 0; i < COUNT; i++) { + char path[1024]; sprintf(path, "hairyhead%d", i); + lfs_file_t file; lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; lfs_file_size(&lfs, &file) => coldsizes[i]; - size = strlen("hair"); + size_t size = strlen("hair"); lfs_off_t j = 0; for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; j += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; memcmp(buffer, "hair", size) => 0; } for (; j < coldsizes[i]; j += size) { + uint8_t buffer[1024]; lfs_file_read(&lfs, &file, buffer, size) => size; memcmp(buffer, "\0\0\0\0", size) => 0; } @@ -393,16 +427,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -[[case]] # noop truncate -define.MEDIUMSIZE = [32, 2048] +# noop truncate +[cases.nop_truncate] +defines.MEDIUMSIZE = [32, 2048] code = ''' - lfs_format(&lfs, &cfg) => 0; - lfs_mount(&lfs, &cfg) => 0; + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_file_t file; lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR | LFS_O_CREAT) => 0; + uint8_t buffer[1024]; strcpy((char*)buffer, "hair"); - size = strlen((char*)buffer); + size_t size = strlen((char*)buffer); for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { lfs_file_write(&lfs, &file, buffer, size) => size; @@ -426,7 +464,7 @@ code = ''' lfs_unmount(&lfs) => 0; // still there after reboot? - lfs_mount(&lfs, &cfg) => 0; + lfs_mount(&lfs, cfg) => 0; lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0; lfs_file_size(&lfs, &file) => MEDIUMSIZE; for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) { |