From 5181ce66cd704b3420fd260943fa07b2ddf7e8ff Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 14 Jan 2020 09:14:01 -0600 Subject: [PATCH] Migrated the first of the tests with internal knowledge Both test_move and test_orphan needed internal knowledge which comes with the addition of the "in" attribute. This was in the plan for the test-revamp from the beginning as it really opens up the ability to write more unit-style-tests using internal knowledge of how littlefs works. More unit-style-tests should help _fix_ bugs by limiting the scope of the test and where the bug could be hiding. The "in" attribute effectively runs tests _inside_ the .c file specified, giving the test access to all static members without needed to change their visibility. --- scripts/test_.py | 228 ++++---- tests_/test_alloc.toml | 38 +- tests_/test_dirs.toml | 26 +- tests_/test_files.toml | 50 +- tests_/test_format.toml | 16 +- tests_/test_interspersed.toml | 52 +- tests_/test_move.toml | 957 ++++++++++++++++++++++++++++++++++ tests_/test_orphan.toml | 56 ++ tests_/test_seek.toml | 48 +- tests_/test_truncate.toml | 29 +- 10 files changed, 1268 insertions(+), 232 deletions(-) create mode 100644 tests_/test_move.toml create mode 100644 tests_/test_orphan.toml diff --git a/scripts/test_.py b/scripts/test_.py index ab674c9..760f64e 100755 --- a/scripts/test_.py +++ b/scripts/test_.py @@ -5,18 +5,20 @@ # # TODO -# - nargs > 1? +# x nargs > 1? # x show perm config on failure # x filtering # n show perm config on verbose? -# - better lineno tracking for cases? +# x better lineno tracking for cases? # n non-int perms? -# - different path format? +# x different path format? # - suite.prologue, suite.epilogue -# - in +# x in # x change BLOCK_CYCLES to -1 by default # x change persist behaviour # x config chaining correct +# - why can't gdb see my defines? +# - say no to internal? import toml import glob @@ -34,17 +36,14 @@ import shlex TESTDIR = 'tests_' RULES = """ define FLATTEN -%$(subst /,.,$(target:.c=.tc)): $(target) - cat <(echo '#line 1 "$$<"') $$< > $$@ +tests_/%$(subst /,.,$(target)): $(target) + ./scripts/explode_asserts.py $$< -o $$@ endef $(foreach target,$(SRC),$(eval $(FLATTEN))) -include tests_/*.d .SECONDARY: -%.c: %.tc - ./scripts/explode_asserts.py $< -o $@ - %.test: override CFLAGS += -fdiagnostics-color=always %.test: override CFLAGS += -ggdb %.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f) @@ -56,7 +55,6 @@ GLOBALS = """ #include "filebd/lfs_filebd.h" #include "rambd/lfs_rambd.h" #include -const char *LFS_DISK = NULL; """ DEFINES = { "LFS_READ_SIZE": 16, @@ -70,6 +68,8 @@ DEFINES = { } PROLOGUE = """ // prologue + extern const char *LFS_DISK; + __attribute__((unused)) lfs_t lfs; __attribute__((unused)) lfs_filebd_t filebd; __attribute__((unused)) lfs_rambd_t rambd; @@ -129,28 +129,31 @@ class TestFailure(Exception): self.assert_ = assert_ class TestCase: - def __init__(self, config, suite=None, caseno=None, lineno=None, **_): + def __init__(self, config, filter=filter, + suite=None, caseno=None, lineno=None, **_): + 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.leaky = config.get('leaky', False) + self.in_ = config.get('in', 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)' % ( + 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]' % ( + return '%s#%d#%d' % ( self.suite.name, self.caseno, self.permno) else: - return '%s[%d]' % ( + return '%s#%d' % ( self.suite.name, self.caseno) def permute(self, defines, permno=None, **_): @@ -163,17 +166,10 @@ class TestCase: def build(self, f, **_): # prologue - f.write('void test_case%d(' % self.caseno) - first = True - for k, v in sorted(self.perms[0].defines.items()): - if k not in self.defines: - if not first: - f.write(',') - else: - first = False - f.write('\n') - f.write(8*' '+'__attribute__((unused)) intmax_t %s' % k) - f.write(') {\n') + f.write('void test_case%d(%s) {\n' % (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))) for k, v in sorted(self.defines.items()): if k not in self.suite.defines: @@ -182,7 +178,7 @@ class TestCase: f.write(PROLOGUE) f.write('\n') f.write(4*' '+'// test case %d\n' % self.caseno) - f.write(4*' '+'#line %d "%s"\n' % (self.lineno, self.suite.path)) + f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path)) # test case goes here f.write(self.code) @@ -198,7 +194,15 @@ class TestCase: f.write('}\n') def shouldtest(self, **args): - if self.if_ is not None: + 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 self.if_ is not None: return eval(self.if_, None, self.defines.copy()) else: return True @@ -227,7 +231,7 @@ class TestCase: ncmd.extend(['-ex', 'up']) elif gdb == 'start': ncmd.extend([ - '-ex', 'b %s:%d' % (self.suite.path, self.lineno), + '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), '-ex', 'r']) ncmd.extend(['--args'] + cmd) @@ -309,7 +313,7 @@ class ReentrantTestCase(TestCase): for cycles in it.count(1): # exact cycle we should drop into debugger? if gdb and failure and failure.cycleno == cycles: - return super().test(exec=exec, persist=True, + return super().test(exec=exec, persist='noerase', gdb=gdb, failure=failure, **args) # run tests, but kill the program after prog/erase has @@ -337,11 +341,12 @@ class ReentrantTestCase(TestCase): raise class TestSuite: - def __init__(self, path, TestCase=TestCase, **args): + def __init__(self, path, filter=None, TestCase=TestCase, **args): self.name = os.path.basename(path) if self.name.endswith('.toml'): self.name = self.name[:-len('.toml')] self.path = path + self.filter = filter self.TestCase = TestCase with open(path) as f: @@ -351,9 +356,12 @@ class TestSuite: # find line numbers f.seek(0) linenos = [] + code_linenos = [] for i, line in enumerate(f): - if re.match(r'^\s*code\s*=\s*(\'\'\'|""")', line): - linenos.append(i + 2) + if re.match(r'\[\[\s*case\s*\]\]', line): + linenos.append(i+1) + if re.match(r'code\s*=\s*(\'\'\'|""")', line): + code_linenos.append(i+2) # grab global config self.defines = config.get('define', {}) @@ -361,12 +369,15 @@ class TestSuite: # 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(0) # give our case's config a copy of our "global" config for k, v in config.items(): if k not in case: case[k] = v # initialize test case - self.cases.append(self.TestCase(case, + self.cases.append(self.TestCase(case, filter=filter, suite=self, caseno=i, lineno=lineno, **args)) def __str__(self): @@ -438,40 +449,52 @@ class TestSuite: return self.perms def build(self, **args): - # build test.c - f = io.StringIO() - f.write(GLOBALS) + # build test files + tf = open(self.path + '.test.c.t', 'w') + tf.write(GLOBALS) + tfs = {None: tf} for case in self.cases: - f.write('\n') - case.build(f, **args) + if case.in_ not in tfs: + tfs[case.in_] = open(self.path+'.'+ + case.in_.replace('/', '.')+'.t', '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) - f.write('\n') - f.write('int main(int argc, char **argv) {\n') - f.write(4*' '+'int case_ = (argc >= 2) ? atoi(argv[1]) : 0;\n') - f.write(4*' '+'int perm = (argc >= 3) ? atoi(argv[2]) : 0;\n') - f.write(4*' '+'LFS_DISK = (argc >= 4) ? argv[3] : NULL;\n') + tfs[case.in_].write('\n') + case.build(tfs[case.in_], **args) + + tf.write('\n') + tf.write('const char *LFS_DISK = NULL;\n') + tf.write('int main(int argc, char **argv) {\n') + tf.write(4*' '+'int case_ = (argc >= 2) ? atoi(argv[1]) : 0;\n') + tf.write(4*' '+'int perm = (argc >= 3) ? atoi(argv[2]) : 0;\n') + tf.write(4*' '+'LFS_DISK = (argc >= 4) ? argv[3] : NULL;\n') for perm in self.perms: - f.write(4*' '+'if (argc < 3 || ' - '(case_ == %d && perm == %d)) { ' % ( - perm.caseno, perm.permno)) - f.write('test_case%d(' % perm.caseno) - first = True - for k, v in sorted(perm.defines.items()): - if k not in perm.case.defines: - if not first: - f.write(', ') - else: - first = False - f.write(str(v)) - f.write('); }\n') - f.write('}\n') + # 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') - # add test-related rules - rules = RULES.replace(4*' ', '\t') + for tf in tfs.values(): + tf.close() + # write makefiles with open(self.path + '.mk', 'w') as mk: - mk.write(rules) + mk.write(RULES.replace(4*' ', '\t')) mk.write('\n') # add truely global defines globally @@ -479,12 +502,18 @@ class TestSuite: mk.write('%s: override CFLAGS += -D%s=%r\n' % ( self.path+'.test', k, v)) - # write test.c in base64 so make can decide when to rebuild - mk.write('%s: %s\n' % (self.path+'.test.tc', self.path)) - mk.write('\t@base64 -d <<< ') - mk.write(base64.b64encode( - f.getvalue().encode('utf8')).decode('utf8')) - mk.write(' > $@\n') + for path in tfs: + if path is None: + mk.write('%s: %s | %s\n' % ( + self.path+'.test.c', + self.path, + self.path+'.test.c.t')) + else: + mk.write('%s: %s %s | %s\n' % ( + self.path+'.'+path.replace('/', '.'), + self.path, path, + self.path+'.'+path.replace('/', '.')+'.t')) + mk.write('\t./scripts/explode_asserts.py $| -o $@\n') self.makefile = self.path + '.mk' self.target = self.path + '.test' @@ -524,37 +553,33 @@ class TestSuite: sys.stdout.write('\n') def main(**args): - testpath = args['testpath'] - - # optional brackets for specific test - m = re.search(r'\[(\d+)(?:,(\d+))?\]$', testpath) - if m: - caseno = int(m.group(1)) - permno = int(m.group(2)) if m.group(2) is not None else None - testpath = testpath[:m.start()] - else: - caseno = None - permno = None - - # figure out the suite's toml file - if os.path.isdir(testpath): - testpath = testpath + '/test_*.toml' - elif os.path.isfile(testpath): - testpath = testpath - elif testpath.endswith('.toml'): - testpath = TESTDIR + '/' + testpath - else: - testpath = TESTDIR + '/' + testpath + '.toml' - - # find tests suites = [] - for path in glob.glob(testpath): - if args.get('valgrind', False): - suites.append(TestSuite(path, TestCase=ValgrindTestCase, **args)) - elif args.get('reentrant', False): - suites.append(TestSuite(path, TestCase=ReentrantTestCase, **args)) + for testpath in args['testpaths']: + # 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 + '/test_*.toml' + elif os.path.isfile(testpath): + testpath = testpath + elif testpath.endswith('.toml'): + testpath = TESTDIR + '/' + testpath else: - suites.append(TestSuite(path, **args)) + testpath = TESTDIR + '/' + testpath + '.toml' + + # find tests + for path in glob.glob(testpath): + if args.get('valgrind', False): + TestCase_ = ValgrindTestCase + elif args.get('reentrant', False): + TestCase_ = ReentrantTestCase + else: + TestCase_ = TestCase + + suites.append(TestSuite(path, + filter=filter, TestCase=TestCase_, **args)) # sort for reproducability suites = sorted(suites) @@ -632,7 +657,7 @@ def main(**args): print('====== testing ======') try: for suite in suites: - suite.test(caseno, permno, **args) + suite.test(**args) except TestFailure: pass @@ -647,11 +672,10 @@ def main(**args): if perm.result == PASS: passed += 1 else: - #sys.stdout.write("--- %s ---\n" % perm) sys.stdout.write( "\033[01m{path}:{lineno}:\033[01;31mfailure:\033[m " "{perm} failed with {returncode}\n".format( - perm=perm, path=perm.suite.path, lineno=perm.lineno-2, + perm=perm, path=perm.suite.path, lineno=perm.lineno, returncode=perm.result.returncode or 0)) if perm.result.stdout: for line in (perm.result.stdout @@ -681,15 +705,15 @@ def main(**args): failure.case.test(failure=failure, **args) sys.exit(0) - print('tests passed: %d' % passed) print('tests failed: %d' % failed) + return 1 if failed > 0 else 0 if __name__ == "__main__": import argparse parser = argparse.ArgumentParser( description="Run parameterized tests in various configurations.") - parser.add_argument('testpath', nargs='?', default=TESTDIR, + parser.add_argument('testpaths', nargs='*', default=[TESTDIR], 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 a \ @@ -713,4 +737,4 @@ if __name__ == "__main__": help="Run reentrant tests with simulated power-loss.") parser.add_argument('-e', '--exec', default=[], type=lambda e: e.split(' '), help="Run tests with another executable prefixed on the command line.") - main(**vars(parser.parse_args())) + sys.exit(main(**vars(parser.parse_args()))) diff --git a/tests_/test_alloc.toml b/tests_/test_alloc.toml index 6851e77..ce80411 100644 --- a/tests_/test_alloc.toml +++ b/tests_/test_alloc.toml @@ -2,6 +2,8 @@ # note for these to work there are many constraints on the device geometry [[case]] # parallel allocation test +define.FILES = 3 +define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' code = ''' const char *names[FILES] = {"bacon", "eggs", "pancakes"}; lfs_file_t files[FILES]; @@ -41,10 +43,10 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' [[case]] # serial allocation test +define.FILES = 3 +define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' code = ''' const char *names[FILES] = {"bacon", "eggs", "pancakes"}; @@ -80,10 +82,11 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' [[case]] # parallel allocation reuse test +define.FILES = 3 +define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' +define.CYCLES = [1, 10] code = ''' const char *names[FILES] = {"bacon", "eggs", "pancakes"}; lfs_file_t files[FILES]; @@ -134,11 +137,11 @@ code = ''' lfs_unmount(&lfs) => 0; } ''' + +[[case]] # serial allocation reuse test define.FILES = 3 define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' define.CYCLES = [1, 10] - -[[case]] # serial allocation reuse test code = ''' const char *names[FILES] = {"bacon", "eggs", "pancakes"}; @@ -185,9 +188,6 @@ code = ''' lfs_unmount(&lfs) => 0; } ''' -define.FILES = 3 -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / FILES)' -define.CYCLES = [1, 10] [[case]] # exhaustion test code = ''' @@ -226,6 +226,7 @@ code = ''' ''' [[case]] # exhaustion wraparound test +define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)' code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -271,7 +272,6 @@ code = ''' lfs_remove(&lfs, "exhaustion") => 0; lfs_unmount(&lfs) => 0; ''' -define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)' [[case]] # dir exhaustion test code = ''' @@ -327,6 +327,9 @@ code = ''' # 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 and LFS_BLOCK_COUNT == 1024' code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -393,11 +396,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' + +[[case]] # split dir test define.LFS_BLOCK_SIZE = 512 define.LFS_BLOCK_COUNT = 1024 if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024' - -[[case]] # split dir test code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -438,11 +441,11 @@ code = ''' lfs_unmount(&lfs) => 0; ''' + +[[case]] # outdated lookahead test define.LFS_BLOCK_SIZE = 512 define.LFS_BLOCK_COUNT = 1024 if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024' - -[[case]] # outdated lookahead test code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -501,11 +504,11 @@ code = ''' } lfs_file_close(&lfs, &file) => 0; ''' + +[[case]] # outdated lookahead and split dir test define.LFS_BLOCK_SIZE = 512 define.LFS_BLOCK_COUNT = 1024 if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024' - -[[case]] # outdated lookahead and split dir test code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -561,6 +564,3 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -define.LFS_BLOCK_SIZE = 512 -define.LFS_BLOCK_COUNT = 1024 -if = 'LFS_BLOCK_SIZE == 512 and LFS_BLOCK_COUNT == 1024' diff --git a/tests_/test_dirs.toml b/tests_/test_dirs.toml index 6598753..712f24f 100644 --- a/tests_/test_dirs.toml +++ b/tests_/test_dirs.toml @@ -15,6 +15,7 @@ code = ''' ''' [[case]] # many directory creation +define.N = 'range(0, 100, 3)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -43,9 +44,9 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; ''' -define.N = 'range(0, 100, 3)' [[case]] # many directory removal +define.N = 'range(3, 100, 11)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -93,9 +94,9 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; ''' -define.N = 'range(3, 100, 11)' [[case]] # many directory rename +define.N = 'range(3, 100, 11)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -152,9 +153,10 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); ''' -define.N = 'range(3, 100, 11)' [[case]] # reentrant many directory creation/rename/removal +define.N = [5, 25] +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -231,10 +233,9 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; ''' -define.N = [5, 25] -reentrant = true [[case]] # file creation +define.N = 'range(3, 100, 11)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -265,9 +266,9 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); ''' -define.N = 'range(3, 100, 11)' [[case]] # file removal +define.N = 'range(0, 100, 3)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -317,9 +318,9 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; ''' -define.N = 'range(0, 100, 3)' [[case]] # file rename +define.N = 'range(0, 100, 3)' code = ''' lfs_format(&lfs, &cfg) => 0; @@ -378,9 +379,10 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs); ''' -define.N = 'range(0, 100, 3)' [[case]] # reentrant file creation/rename/removal +define.N = [5, 25] +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -457,8 +459,6 @@ code = ''' lfs_dir_close(&lfs, &dir) => 0; lfs_unmount(&lfs) => 0; ''' -define.N = [5, 25] -reentrant = true [[case]] # nested directories code = ''' @@ -585,6 +585,7 @@ code = ''' ''' [[case]] # recursive remove +define.N = [10, 100] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -639,7 +640,6 @@ code = ''' lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT; lfs_unmount(&lfs) => 0; ''' -define.N = [10, 100] [[case]] # other error cases code = ''' @@ -716,6 +716,7 @@ code = ''' ''' [[case]] # directory seek +define.COUNT = [4, 128, 132] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -774,9 +775,9 @@ code = ''' lfs_unmount(&lfs) => 0; } ''' -define.COUNT = [4, 128, 132] [[case]] # root seek +define.COUNT = [4, 128, 132] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -834,5 +835,4 @@ code = ''' lfs_unmount(&lfs) => 0; } ''' -define.COUNT = [4, 128, 132] diff --git a/tests_/test_files.toml b/tests_/test_files.toml index ce16178..da132f2 100644 --- a/tests_/test_files.toml +++ b/tests_/test_files.toml @@ -20,6 +20,8 @@ code = ''' ''' [[case]] # larger files +define.SIZE = [32, 8192, 262144, 0, 7, 8193] +define.CHUNKSIZE = [31, 16, 33, 1, 1023] code = ''' lfs_format(&lfs, &cfg) => 0; @@ -54,10 +56,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.SIZE = [32, 8192, 262144, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 33, 1, 1023] [[case]] # rewriting files +define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] +define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] +define.CHUNKSIZE = [31, 16, 1, 1025] code = ''' lfs_format(&lfs, &cfg) => 0; @@ -135,11 +138,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; 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, 1025] - -[[case]] # appending files code = ''' lfs_format(&lfs, &cfg) => 0; @@ -212,11 +215,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; 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, 1025] - -[[case]] # truncating files code = ''' lfs_format(&lfs, &cfg) => 0; @@ -281,11 +284,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -define.CHUNKSIZE = [31, 16, 1, 1025] [[case]] # reentrant file writing +define.SIZE = [32, 0, 7, 2049] +define.CHUNKSIZE = [31, 16, 65] +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -329,11 +332,17 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.SIZE = [32, 0, 7, 2049] -define.CHUNKSIZE = [31, 16, 65] -reentrant = true [[case]] # reentrant file writing with syncs +define = [ + # append (O(n)) + {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, + # truncate (O(n^2)) + {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, + # rewrite (O(n^2)) + {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, +] +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -393,17 +402,9 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define = [ - # append (O(n)) - {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, - # truncate (O(n^2)) - {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, - # rewrite (O(n^2)) - {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, -] -reentrant = true [[case]] # many files +define.N = 300 code = ''' lfs_format(&lfs, &cfg) => 0; // create N files of 7 bytes @@ -426,9 +427,9 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -define.N = 300 [[case]] # many files with power cycle +define.N = 300 code = ''' lfs_format(&lfs, &cfg) => 0; // create N files of 7 bytes @@ -453,9 +454,10 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -define.N = 300 [[case]] # many files with power loss +define.N = 300 +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -482,5 +484,3 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -define.N = 300 -reentrant = true diff --git a/tests_/test_format.toml b/tests_/test_format.toml index c52db2a..702c039 100644 --- a/tests_/test_format.toml +++ b/tests_/test_format.toml @@ -11,6 +11,7 @@ code = ''' ''' [[case]] # reentrant format +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -19,7 +20,6 @@ code = ''' } lfs_unmount(&lfs) => 0; ''' -reentrant = true [[case]] # invalid mount code = ''' @@ -29,6 +29,8 @@ code = ''' # TODO invalid superblock? (corrupt 1, 0) [[case]] # expanding superblock +define.BLOCK_CYCLES = [32, 33, 1] +define.N = [10, 100, 1000] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -47,10 +49,10 @@ code = ''' assert(strcmp(info.name, "dummy") == 0); lfs_unmount(&lfs) => 0; ''' -define.BLOCK_CYCLES = [32, 33, 1] -define.N = [10, 100, 1000] [[case]] # expanding superblock with power cycle +define.BLOCK_CYCLES = [32, 33, 1] +define.N = [10, 100, 1000] code = ''' lfs_format(&lfs, &cfg) => 0; for (int i = 0; i < N; i++) { @@ -71,10 +73,11 @@ code = ''' assert(strcmp(info.name, "dummy") == 0); lfs_unmount(&lfs) => 0; ''' -define.BLOCK_CYCLES = [32, 33, 1] -define.N = [10, 100, 1000] [[case]] # reentrant expanding superblock +define.BLOCK_CYCLES = [2, 1] +define.N = 24 +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -100,6 +103,3 @@ code = ''' assert(strcmp(info.name, "dummy") == 0); lfs_unmount(&lfs) => 0; ''' -define.BLOCK_CYCLES = [2, 1] -define.N = 24 -reentrant = true diff --git a/tests_/test_interspersed.toml b/tests_/test_interspersed.toml index ef4f9d2..32f79e7 100644 --- a/tests_/test_interspersed.toml +++ b/tests_/test_interspersed.toml @@ -1,5 +1,16 @@ [[case]] # interspersed file test +# TODO FILES=26 found bug +#define.SIZE = [10, 100] +#define.FILES = [4, 10, 26] +define = [ + {SIZE=10, FILES=4}, + {SIZE=10, FILES=10}, + #{SIZE=10, FILES=26}, + {SIZE=100, FILES=4}, + {SIZE=100, FILES=10}, + #{SIZE=100, FILES=26}, +] code = ''' lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; @@ -56,19 +67,10 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# TODO FILES=26 found bug -#define.SIZE = [10, 100] -#define.FILES = [4, 10, 26] -define = [ - {SIZE=10, FILES=4}, - {SIZE=10, FILES=10}, - #{SIZE=10, FILES=26}, - {SIZE=100, FILES=4}, - {SIZE=100, FILES=10}, - #{SIZE=100, FILES=26}, -] [[case]] # interspersed remove file test +define.SIZE = [10, 100] +define.FILES = [4, 10, 26] code = ''' const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; lfs_format(&lfs, &cfg) => 0; @@ -118,10 +120,9 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -define.SIZE = [10, 100] -define.FILES = [4, 10, 26] [[case]] # remove inconveniently test +define.SIZE = [10, 100] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -179,9 +180,20 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -define.SIZE = [10, 100] [[case]] # reentrant interspersed file test +# TODO FILES=26 found bug +#define.SIZE = [10, 100] +#define.FILES = [4, 10, 26] +define = [ + {SIZE=10, FILES=4}, + {SIZE=10, FILES=10}, + #{SIZE=10, FILES=26}, + {SIZE=100, FILES=4}, + #{SIZE=100, FILES=10}, + #{SIZE=100, FILES=26}, +] +reentrant = true code = ''' lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; @@ -248,15 +260,3 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# TODO FILES=26 found bug -#define.SIZE = [10, 100] -#define.FILES = [4, 10, 26] -define = [ - {SIZE=10, FILES=4}, - {SIZE=10, FILES=10}, - #{SIZE=10, FILES=26}, - {SIZE=100, FILES=4}, - #{SIZE=100, FILES=10}, - #{SIZE=100, FILES=26}, -] -reentrant = true diff --git a/tests_/test_move.toml b/tests_/test_move.toml new file mode 100644 index 0000000..c49b038 --- /dev/null +++ b/tests_/test_move.toml @@ -0,0 +1,957 @@ +[[case]] # move file +code = ''' + 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_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; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "a/hello", "c/hello") => 0; + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 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) => 0; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move file corrupt source +in = "lfs.c" +code = ''' + 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_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; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + lfs_mount(&lfs, &cfg) => 0; + lfs_dir_open(&lfs, &dir, "a") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 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) => 0; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move file corrupt source and dest +in = "lfs.c" +code = ''' + 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_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; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + // corrupt the destination + 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) { + 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; + + lfs_mount(&lfs, &cfg) => 0; + lfs_dir_open(&lfs, &dir, "a") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => 0; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + 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) => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move file after corrupt +in = "lfs.c" +code = ''' + 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_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; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + // corrupt the destination + 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) { + 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; + + // continue move + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "a/hello", "c/hello") => 0; + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 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) => 0; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # simple reentrant move file +reentrant = true +code = ''' + err = lfs_mount(&lfs, &cfg); + if (err) { + lfs_format(&lfs, &cfg) => 0; + lfs_mount(&lfs, &cfg) => 0; + } + err = lfs_mkdir(&lfs, "a"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "b"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "c"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "d"); + assert(!err || err == LFS_ERR_EXIST); + lfs_unmount(&lfs) => 0; + + while (true) { + lfs_mount(&lfs, &cfg) => 0; + // there should never exist _2_ hello files + int count = 0; + if (lfs_stat(&lfs, "a/hello", &info) == 0) { + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6 || info.size == 0); + count += 1; + } + if (lfs_stat(&lfs, "b/hello", &info) == 0) { + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + count += 1; + } + if (lfs_stat(&lfs, "c/hello", &info) == 0) { + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + count += 1; + } + if (lfs_stat(&lfs, "d/hello", &info) == 0) { + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + count += 1; + } + assert(count <= 1); + lfs_unmount(&lfs) => 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) { + lfs_rename(&lfs, "b/hello", "c/hello") => 0; + } else if (lfs_stat(&lfs, "c/hello", &info) == 0) { + lfs_rename(&lfs, "c/hello", "d/hello") => 0; + } else if (lfs_stat(&lfs, "d/hello", &info) == 0) { + // success + break; + } else { + // create file + lfs_file_open(&lfs, &file, "a/hello", + LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs_file_write(&lfs, &file, "hola\n", 5) => 5; + lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "d") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == 5+8+6); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 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; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move dir +code = ''' + 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_mkdir(&lfs, "a/hi") => 0; + lfs_mkdir(&lfs, "a/hi/hola") => 0; + lfs_mkdir(&lfs, "a/hi/bonjour") => 0; + lfs_mkdir(&lfs, "a/hi/ohayo") => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "a/hi", "c/hi") => 0; + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "c/hi") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "bonjour") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hola") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "ohayo") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move dir corrupt source +in = "lfs.c" +code = ''' + 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_mkdir(&lfs, "a/hi") => 0; + lfs_mkdir(&lfs, "a/hi/hola") => 0; + lfs_mkdir(&lfs, "a/hi/bonjour") => 0; + lfs_mkdir(&lfs, "a/hi/ohayo") => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + lfs_mount(&lfs, &cfg) => 0; + lfs_dir_open(&lfs, &dir, "a") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "c/hi") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "bonjour") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hola") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "ohayo") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move dir corrupt source and dest +in = "lfs.c" +code = ''' + 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_mkdir(&lfs, "a/hi") => 0; + lfs_mkdir(&lfs, "a/hi/hola") => 0; + lfs_mkdir(&lfs, "a/hi/bonjour") => 0; + lfs_mkdir(&lfs, "a/hi/ohayo") => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + // corrupt the destination + 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) { + 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; + + lfs_mount(&lfs, &cfg) => 0; + lfs_dir_open(&lfs, &dir, "a") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_dir_open(&lfs, &dir, "a/hi") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "bonjour") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hola") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "ohayo") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "c/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move dir after corrupt +in = "lfs.c" +code = ''' + 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_mkdir(&lfs, "a/hi") => 0; + lfs_mkdir(&lfs, "a/hi/hola") => 0; + lfs_mkdir(&lfs, "a/hi/bonjour") => 0; + lfs_mkdir(&lfs, "a/hi/ohayo") => 0; + lfs_unmount(&lfs) => 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_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) { + 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; + + // corrupt the destination + 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) { + 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; + + // continue move + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "a/hi", "c/hi") => 0; + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "c") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "c/hi") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "bonjour") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hola") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "ohayo") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # simple reentrant move dir +reentrant = true +code = ''' + err = lfs_mount(&lfs, &cfg); + if (err) { + lfs_format(&lfs, &cfg) => 0; + lfs_mount(&lfs, &cfg) => 0; + } + err = lfs_mkdir(&lfs, "a"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "b"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "c"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "d"); + assert(!err || err == LFS_ERR_EXIST); + lfs_unmount(&lfs) => 0; + + while (true) { + lfs_mount(&lfs, &cfg) => 0; + // there should never exist _2_ hi directories + int count = 0; + if (lfs_stat(&lfs, "a/hi", &info) == 0) { + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + count += 1; + } + if (lfs_stat(&lfs, "b/hi", &info) == 0) { + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + count += 1; + } + if (lfs_stat(&lfs, "c/hi", &info) == 0) { + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + count += 1; + } + if (lfs_stat(&lfs, "d/hi", &info) == 0) { + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + count += 1; + } + assert(count <= 1); + lfs_unmount(&lfs) => 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) { + lfs_rename(&lfs, "b/hi", "c/hi") => 0; + } else if (lfs_stat(&lfs, "c/hi", &info) == 0) { + lfs_rename(&lfs, "c/hi", "d/hi") => 0; + } else if (lfs_stat(&lfs, "d/hi", &info) == 0) { + break; // success + } else { + // create dir and rename for atomicity + err = lfs_mkdir(&lfs, "temp"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "temp/hola"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "temp/bonjour"); + assert(!err || err == LFS_ERR_EXIST); + err = lfs_mkdir(&lfs, "temp/ohayo"); + assert(!err || err == LFS_ERR_EXIST); + lfs_rename(&lfs, "temp", "a/hi") => 0; + } + lfs_unmount(&lfs) => 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); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_dir_open(&lfs, &dir, "d") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hi") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + + lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "c/hi") => LFS_ERR_NOENT; + lfs_dir_open(&lfs, &dir, "d/hi") => 0; + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "bonjour") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "hola") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 1; + assert(strcmp(info.name, "ohayo") == 0); + assert(info.type == LFS_TYPE_DIR); + lfs_dir_read(&lfs, &dir, &info) => 0; + lfs_dir_close(&lfs, &dir) => 0; + lfs_unmount(&lfs) => 0; +''' + +[[case]] # move state stealing +code = ''' + 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_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; + lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "a/hello", "b/hello") => 0; + lfs_unmount(&lfs) => 0; + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "b/hello", "c/hello") => 0; + lfs_unmount(&lfs) => 0; + lfs_mount(&lfs, &cfg) => 0; + lfs_rename(&lfs, "c/hello", "d/hello") => 0; + lfs_unmount(&lfs) => 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; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 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_stat(&lfs, "a", &info) => 0; + lfs_stat(&lfs, "b", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "c", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "d", &info) => 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; + lfs_file_read(&lfs, &file, buffer, 5) => 5; + memcmp(buffer, "hola\n", 5) => 0; + lfs_file_read(&lfs, &file, buffer, 8) => 8; + memcmp(buffer, "bonjour\n", 8) => 0; + lfs_file_read(&lfs, &file, buffer, 6) => 6; + memcmp(buffer, "ohayo\n", 6) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; +''' diff --git a/tests_/test_orphan.toml b/tests_/test_orphan.toml new file mode 100644 index 0000000..fe52199 --- /dev/null +++ b/tests_/test_orphan.toml @@ -0,0 +1,56 @@ +[[case]] # orphan test +in = "lfs.c" +code = ''' + 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; + lfs_remove(&lfs, "parent/orphan") => 0; + lfs_unmount(&lfs) => 0; + + // 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_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) { + 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; + + 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; + lfs_unmount(&lfs) => 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; + // this mkdir should both create a dir and deorphan, so size + // should be unchanged + lfs_mkdir(&lfs, "parent/otherchild") => 0; + lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; + lfs_stat(&lfs, "parent/child", &info) => 0; + lfs_stat(&lfs, "parent/otherchild", &info) => 0; + lfs_fs_size(&lfs) => 8; + lfs_unmount(&lfs) => 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; + lfs_fs_size(&lfs) => 8; + lfs_unmount(&lfs) => 0; +''' diff --git a/tests_/test_seek.toml b/tests_/test_seek.toml index 0a1d90d..586ab71 100644 --- a/tests_/test_seek.toml +++ b/tests_/test_seek.toml @@ -1,5 +1,13 @@ [[case]] # simple file seek +define = [ + {COUNT=132, SKIP=4}, + {COUNT=132, SKIP=128}, + {COUNT=200, SKIP=10}, + {COUNT=200, SKIP=100}, + {COUNT=4, SKIP=1}, + {COUNT=4, SKIP=2}, +] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -59,6 +67,8 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' + +[[case]] # simple file seek and write define = [ {COUNT=132, SKIP=4}, {COUNT=132, SKIP=128}, @@ -67,8 +77,6 @@ define = [ {COUNT=4, SKIP=1}, {COUNT=4, SKIP=2}, ] - -[[case]] # simple file seek and write code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -120,16 +128,10 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define = [ - {COUNT=132, SKIP=4}, - {COUNT=132, SKIP=128}, - {COUNT=200, SKIP=10}, - {COUNT=200, SKIP=100}, - {COUNT=4, SKIP=1}, - {COUNT=4, SKIP=2}, -] [[case]] # boundary seek and writes +define.COUNT = 132 +define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"' code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -180,10 +182,16 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.COUNT = 132 -define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"' [[case]] # out of bounds seek +define = [ + {COUNT=132, SKIP=4}, + {COUNT=132, SKIP=128}, + {COUNT=200, SKIP=10}, + {COUNT=200, SKIP=100}, + {COUNT=4, SKIP=2}, + {COUNT=4, SKIP=3}, +] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -229,16 +237,9 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define = [ - {COUNT=132, SKIP=4}, - {COUNT=132, SKIP=128}, - {COUNT=200, SKIP=10}, - {COUNT=200, SKIP=100}, - {COUNT=4, SKIP=2}, - {COUNT=4, SKIP=3}, -] [[case]] # inline write and seek +define.SIZE = [2, 4, 128, 132] code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -303,9 +304,11 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.SIZE = [2, 4, 128, 132] [[case]] # file seek and write with power-loss +# must be power-of-2 for quadratic probing to be exhaustive +define.COUNT = [4, 64, 128] +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -375,6 +378,3 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -# must be power-of-2 for quadratic probing to be exhaustive -define.COUNT = [4, 64, 128] -reentrant = true diff --git a/tests_/test_truncate.toml b/tests_/test_truncate.toml index fde2da5..4416986 100644 --- a/tests_/test_truncate.toml +++ b/tests_/test_truncate.toml @@ -1,4 +1,6 @@ [[case]] # simple truncate +define.MEDIUMSIZE = [32, 2048] +define.LARGESIZE = 8192 code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -39,10 +41,10 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 [[case]] # truncate and read +define.MEDIUMSIZE = [32, 2048] +define.LARGESIZE = 8192 code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -90,8 +92,6 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 [[case]] # write, truncate, and read code = ''' @@ -146,6 +146,8 @@ code = ''' ''' [[case]] # truncate and write +define.MEDIUMSIZE = [32, 2048] +define.LARGESIZE = 8192 code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; @@ -193,10 +195,12 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' -define.MEDIUMSIZE = [32, 2048] -define.LARGESIZE = 8192 [[case]] # truncate write under powerloss +define.SMALLSIZE = [4, 512] +define.MEDIUMSIZE = [32, 1024] +define.LARGESIZE = 2048 +reentrant = true code = ''' err = lfs_mount(&lfs, &cfg); if (err) { @@ -257,12 +261,12 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -define.SMALLSIZE = [4, 512] -define.MEDIUMSIZE = [32, 1024] -define.LARGESIZE = 2048 -reentrant = true [[case]] # more aggressive general truncation tests +define.CONFIG = 'range(6)' +define.SMALLSIZE = 32 +define.MEDIUMSIZE = 2048 +define.LARGESIZE = 8192 code = ''' #define COUNT 5 const struct { @@ -388,8 +392,3 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -define.CONFIG = 'range(6)' -define.SMALLSIZE = 32 -define.MEDIUMSIZE = 2048 -define.LARGESIZE = 8192 -