1# Test for compatibility between different littlefs versions
2#
3# Note, these tests are a bit special. They expect to be linked against two
4# different versions of littlefs:
5# - lfs  => the new/current version of littlefs
6# - lfsp => the previous version of littlefs
7#
8# If lfsp is not linked, and LFSP is not defined, these tests will alias
9# the relevant lfs types/functions as necessary so at least the tests can
10# themselves be tested locally.
11#
12# But to get value from these tests, it's expected that the previous version
13# of littlefs be linked in during CI, with the help of scripts/changeprefix.py
14#
15
16# alias littlefs symbols as needed
17#
18# there may be a better way to do this, but oh well, explicit aliases works
19code = '''
20#ifdef LFSP
21#define STRINGIZE(x) STRINGIZE_(x)
22#define STRINGIZE_(x) #x
23#include STRINGIZE(LFSP)
24#else
25#define LFSP_DISK_VERSION LFS_DISK_VERSION
26#define LFSP_DISK_VERSION_MAJOR LFS_DISK_VERSION_MAJOR
27#define LFSP_DISK_VERSION_MINOR LFS_DISK_VERSION_MINOR
28#define lfsp_t lfs_t
29#define lfsp_config lfs_config
30#define lfsp_format lfs_format
31#define lfsp_mount lfs_mount
32#define lfsp_unmount lfs_unmount
33#define lfsp_fsinfo lfs_fsinfo
34#define lfsp_fs_stat lfs_fs_stat
35#define lfsp_dir_t lfs_dir_t
36#define lfsp_info lfs_info
37#define LFSP_TYPE_REG LFS_TYPE_REG
38#define LFSP_TYPE_DIR LFS_TYPE_DIR
39#define lfsp_mkdir lfs_mkdir
40#define lfsp_dir_open lfs_dir_open
41#define lfsp_dir_read lfs_dir_read
42#define lfsp_dir_close lfs_dir_close
43#define lfsp_file_t lfs_file_t
44#define LFSP_O_RDONLY LFS_O_RDONLY
45#define LFSP_O_WRONLY LFS_O_WRONLY
46#define LFSP_O_CREAT LFS_O_CREAT
47#define LFSP_O_EXCL LFS_O_EXCL
48#define LFSP_SEEK_SET LFS_SEEK_SET
49#define lfsp_file_open lfs_file_open
50#define lfsp_file_write lfs_file_write
51#define lfsp_file_read lfs_file_read
52#define lfsp_file_seek lfs_file_seek
53#define lfsp_file_close lfs_file_close
54#endif
55'''
56
57
58
59## forward-compatibility tests ##
60
61# test we can mount in a new version
62[cases.test_compat_forward_mount]
63if = '''
64    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
65        && DISK_VERSION == 0
66'''
67code = '''
68    // create the previous version
69    struct lfsp_config cfgp;
70    memcpy(&cfgp, cfg, sizeof(cfgp));
71    lfsp_t lfsp;
72    lfsp_format(&lfsp, &cfgp) => 0;
73
74    // confirm the previous mount works
75    lfsp_mount(&lfsp, &cfgp) => 0;
76    lfsp_unmount(&lfsp) => 0;
77
78
79    // now test the new mount
80    lfs_t lfs;
81    lfs_mount(&lfs, cfg) => 0;
82
83    // we should be able to read the version using lfs_fs_stat
84    struct lfs_fsinfo fsinfo;
85    lfs_fs_stat(&lfs, &fsinfo) => 0;
86    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
87
88    lfs_unmount(&lfs) => 0;
89'''
90
91# test we can read dirs in a new version
92[cases.test_compat_forward_read_dirs]
93defines.COUNT = 5
94if = '''
95    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
96        && DISK_VERSION == 0
97'''
98code = '''
99    // create the previous version
100    struct lfsp_config cfgp;
101    memcpy(&cfgp, cfg, sizeof(cfgp));
102    lfsp_t lfsp;
103    lfsp_format(&lfsp, &cfgp) => 0;
104
105    // write COUNT dirs
106    lfsp_mount(&lfsp, &cfgp) => 0;
107    for (lfs_size_t i = 0; i < COUNT; i++) {
108        char name[8];
109        sprintf(name, "dir%03d", i);
110        lfsp_mkdir(&lfsp, name) => 0;
111    }
112    lfsp_unmount(&lfsp) => 0;
113
114
115    // mount the new version
116    lfs_t lfs;
117    lfs_mount(&lfs, cfg) => 0;
118
119    // we should be able to read the version using lfs_fs_stat
120    struct lfs_fsinfo fsinfo;
121    lfs_fs_stat(&lfs, &fsinfo) => 0;
122    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
123
124    // can we list the directories?
125    lfs_dir_t dir;
126    lfs_dir_open(&lfs, &dir, "/") => 0;
127    struct lfs_info info;
128    lfs_dir_read(&lfs, &dir, &info) => 1;
129    assert(info.type == LFS_TYPE_DIR);
130    assert(strcmp(info.name, ".") == 0);
131    lfs_dir_read(&lfs, &dir, &info) => 1;
132    assert(info.type == LFS_TYPE_DIR);
133    assert(strcmp(info.name, "..") == 0);
134
135    for (lfs_size_t i = 0; i < COUNT; i++) {
136        lfs_dir_read(&lfs, &dir, &info) => 1;
137        assert(info.type == LFS_TYPE_DIR);
138        char name[8];
139        sprintf(name, "dir%03d", i);
140        assert(strcmp(info.name, name) == 0);
141    }
142
143    lfs_dir_read(&lfs, &dir, &info) => 0;
144    lfs_dir_close(&lfs, &dir) => 0;
145
146    lfs_unmount(&lfs) => 0;
147'''
148
149# test we can read files in a new version
150[cases.test_compat_forward_read_files]
151defines.COUNT = 5
152defines.SIZE = [4, 32, 512, 8192]
153defines.CHUNK = 4
154if = '''
155    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
156        && DISK_VERSION == 0
157'''
158code = '''
159    // create the previous version
160    struct lfsp_config cfgp;
161    memcpy(&cfgp, cfg, sizeof(cfgp));
162    lfsp_t lfsp;
163    lfsp_format(&lfsp, &cfgp) => 0;
164
165    // write COUNT files
166    lfsp_mount(&lfsp, &cfgp) => 0;
167    uint32_t prng = 42;
168    for (lfs_size_t i = 0; i < COUNT; i++) {
169        lfsp_file_t file;
170        char name[8];
171        sprintf(name, "file%03d", i);
172        lfsp_file_open(&lfsp, &file, name,
173                LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
174        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
175            uint8_t chunk[CHUNK];
176            for (lfs_size_t k = 0; k < CHUNK; k++) {
177                chunk[k] = TEST_PRNG(&prng) & 0xff;
178            }
179
180            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
181        }
182        lfsp_file_close(&lfsp, &file) => 0;
183    }
184    lfsp_unmount(&lfsp) => 0;
185
186
187    // mount the new version
188    lfs_t lfs;
189    lfs_mount(&lfs, cfg) => 0;
190
191    // we should be able to read the version using lfs_fs_stat
192    struct lfs_fsinfo fsinfo;
193    lfs_fs_stat(&lfs, &fsinfo) => 0;
194    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
195
196    // can we list the files?
197    lfs_dir_t dir;
198    lfs_dir_open(&lfs, &dir, "/") => 0;
199    struct lfs_info info;
200    lfs_dir_read(&lfs, &dir, &info) => 1;
201    assert(info.type == LFS_TYPE_DIR);
202    assert(strcmp(info.name, ".") == 0);
203    lfs_dir_read(&lfs, &dir, &info) => 1;
204    assert(info.type == LFS_TYPE_DIR);
205    assert(strcmp(info.name, "..") == 0);
206
207    for (lfs_size_t i = 0; i < COUNT; i++) {
208        lfs_dir_read(&lfs, &dir, &info) => 1;
209        assert(info.type == LFS_TYPE_REG);
210        char name[8];
211        sprintf(name, "file%03d", i);
212        assert(strcmp(info.name, name) == 0);
213        assert(info.size == SIZE);
214    }
215
216    lfs_dir_read(&lfs, &dir, &info) => 0;
217
218    // now can we read the files?
219    prng = 42;
220    for (lfs_size_t i = 0; i < COUNT; i++) {
221        lfs_file_t file;
222        char name[8];
223        sprintf(name, "file%03d", i);
224        lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
225        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
226            uint8_t chunk[CHUNK];
227            lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
228
229            for (lfs_size_t k = 0; k < CHUNK; k++) {
230                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
231            }
232        }
233        lfs_file_close(&lfs, &file) => 0;
234    }
235
236    lfs_unmount(&lfs) => 0;
237'''
238
239# test we can read files in dirs in a new version
240[cases.test_compat_forward_read_files_in_dirs]
241defines.COUNT = 5
242defines.SIZE = [4, 32, 512, 8192]
243defines.CHUNK = 4
244if = '''
245    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
246        && DISK_VERSION == 0
247'''
248code = '''
249    // create the previous version
250    struct lfsp_config cfgp;
251    memcpy(&cfgp, cfg, sizeof(cfgp));
252    lfsp_t lfsp;
253    lfsp_format(&lfsp, &cfgp) => 0;
254
255    // write COUNT files+dirs
256    lfsp_mount(&lfsp, &cfgp) => 0;
257    uint32_t prng = 42;
258    for (lfs_size_t i = 0; i < COUNT; i++) {
259        char name[16];
260        sprintf(name, "dir%03d", i);
261        lfsp_mkdir(&lfsp, name) => 0;
262
263        lfsp_file_t file;
264        sprintf(name, "dir%03d/file%03d", i, i);
265        lfsp_file_open(&lfsp, &file, name,
266                LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
267        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
268            uint8_t chunk[CHUNK];
269            for (lfs_size_t k = 0; k < CHUNK; k++) {
270                chunk[k] = TEST_PRNG(&prng) & 0xff;
271            }
272
273            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
274        }
275        lfsp_file_close(&lfsp, &file) => 0;
276    }
277    lfsp_unmount(&lfsp) => 0;
278
279
280    // mount the new version
281    lfs_t lfs;
282    lfs_mount(&lfs, cfg) => 0;
283
284    // we should be able to read the version using lfs_fs_stat
285    struct lfs_fsinfo fsinfo;
286    lfs_fs_stat(&lfs, &fsinfo) => 0;
287    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
288
289    // can we list the directories?
290    lfs_dir_t dir;
291    lfs_dir_open(&lfs, &dir, "/") => 0;
292    struct lfs_info info;
293    lfs_dir_read(&lfs, &dir, &info) => 1;
294    assert(info.type == LFS_TYPE_DIR);
295    assert(strcmp(info.name, ".") == 0);
296    lfs_dir_read(&lfs, &dir, &info) => 1;
297    assert(info.type == LFS_TYPE_DIR);
298    assert(strcmp(info.name, "..") == 0);
299
300    for (lfs_size_t i = 0; i < COUNT; i++) {
301        lfs_dir_read(&lfs, &dir, &info) => 1;
302        assert(info.type == LFS_TYPE_DIR);
303        char name[8];
304        sprintf(name, "dir%03d", i);
305        assert(strcmp(info.name, name) == 0);
306    }
307
308    lfs_dir_read(&lfs, &dir, &info) => 0;
309    lfs_dir_close(&lfs, &dir) => 0;
310
311    // can we list the files?
312    for (lfs_size_t i = 0; i < COUNT; i++) {
313        char name[8];
314        sprintf(name, "dir%03d", i);
315        lfs_dir_t dir;
316        lfs_dir_open(&lfs, &dir, name) => 0;
317        struct lfs_info info;
318        lfs_dir_read(&lfs, &dir, &info) => 1;
319        assert(info.type == LFS_TYPE_DIR);
320        assert(strcmp(info.name, ".") == 0);
321        lfs_dir_read(&lfs, &dir, &info) => 1;
322        assert(info.type == LFS_TYPE_DIR);
323        assert(strcmp(info.name, "..") == 0);
324
325        lfs_dir_read(&lfs, &dir, &info) => 1;
326        assert(info.type == LFS_TYPE_REG);
327        sprintf(name, "file%03d", i);
328        assert(strcmp(info.name, name) == 0);
329        assert(info.size == SIZE);
330
331        lfs_dir_read(&lfs, &dir, &info) => 0;
332        lfs_dir_close(&lfs, &dir) => 0;
333    }
334
335    // now can we read the files?
336    prng = 42;
337    for (lfs_size_t i = 0; i < COUNT; i++) {
338        lfs_file_t file;
339        char name[16];
340        sprintf(name, "dir%03d/file%03d", i, i);
341        lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
342        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
343            uint8_t chunk[CHUNK];
344            lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
345
346            for (lfs_size_t k = 0; k < CHUNK; k++) {
347                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
348            }
349        }
350        lfs_file_close(&lfs, &file) => 0;
351    }
352
353    lfs_unmount(&lfs) => 0;
354'''
355
356# test we can write dirs in a new version
357[cases.test_compat_forward_write_dirs]
358defines.COUNT = 10
359if = '''
360    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
361        && DISK_VERSION == 0
362'''
363code = '''
364    // create the previous version
365    struct lfsp_config cfgp;
366    memcpy(&cfgp, cfg, sizeof(cfgp));
367    lfsp_t lfsp;
368    lfsp_format(&lfsp, &cfgp) => 0;
369
370    // write COUNT/2 dirs
371    lfsp_mount(&lfsp, &cfgp) => 0;
372    for (lfs_size_t i = 0; i < COUNT/2; i++) {
373        char name[8];
374        sprintf(name, "dir%03d", i);
375        lfsp_mkdir(&lfsp, name) => 0;
376    }
377    lfsp_unmount(&lfsp) => 0;
378
379
380    // mount the new version
381    lfs_t lfs;
382    lfs_mount(&lfs, cfg) => 0;
383
384    // we should be able to read the version using lfs_fs_stat
385    struct lfs_fsinfo fsinfo;
386    lfs_fs_stat(&lfs, &fsinfo) => 0;
387    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
388
389    // write another COUNT/2 dirs
390    for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
391        char name[8];
392        sprintf(name, "dir%03d", i);
393        lfs_mkdir(&lfs, name) => 0;
394    }
395
396    // can we list the directories?
397    lfs_dir_t dir;
398    lfs_dir_open(&lfs, &dir, "/") => 0;
399    struct lfs_info info;
400    lfs_dir_read(&lfs, &dir, &info) => 1;
401    assert(info.type == LFS_TYPE_DIR);
402    assert(strcmp(info.name, ".") == 0);
403    lfs_dir_read(&lfs, &dir, &info) => 1;
404    assert(info.type == LFS_TYPE_DIR);
405    assert(strcmp(info.name, "..") == 0);
406
407    for (lfs_size_t i = 0; i < COUNT; i++) {
408        lfs_dir_read(&lfs, &dir, &info) => 1;
409        assert(info.type == LFS_TYPE_DIR);
410        char name[8];
411        sprintf(name, "dir%03d", i);
412        assert(strcmp(info.name, name) == 0);
413    }
414
415    lfs_dir_read(&lfs, &dir, &info) => 0;
416    lfs_dir_close(&lfs, &dir) => 0;
417
418    lfs_unmount(&lfs) => 0;
419'''
420
421# test we can write files in a new version
422[cases.test_compat_forward_write_files]
423defines.COUNT = 5
424defines.SIZE = [4, 32, 512, 8192]
425defines.CHUNK = 2
426if = '''
427    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
428        && DISK_VERSION == 0
429'''
430code = '''
431    // create the previous version
432    struct lfsp_config cfgp;
433    memcpy(&cfgp, cfg, sizeof(cfgp));
434    lfsp_t lfsp;
435    lfsp_format(&lfsp, &cfgp) => 0;
436
437    // write half COUNT files
438    lfsp_mount(&lfsp, &cfgp) => 0;
439    uint32_t prng = 42;
440    for (lfs_size_t i = 0; i < COUNT; i++) {
441        // write half
442        lfsp_file_t file;
443        char name[8];
444        sprintf(name, "file%03d", i);
445        lfsp_file_open(&lfsp, &file, name,
446                LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
447        for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
448            uint8_t chunk[CHUNK];
449            for (lfs_size_t k = 0; k < CHUNK; k++) {
450                chunk[k] = TEST_PRNG(&prng) & 0xff;
451            }
452
453            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
454        }
455        lfsp_file_close(&lfsp, &file) => 0;
456
457        // skip the other half but keep our prng reproducible
458        for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
459            TEST_PRNG(&prng);
460        }
461    }
462    lfsp_unmount(&lfsp) => 0;
463
464
465    // mount the new version
466    lfs_t lfs;
467    lfs_mount(&lfs, cfg) => 0;
468
469    // we should be able to read the version using lfs_fs_stat
470    struct lfs_fsinfo fsinfo;
471    lfs_fs_stat(&lfs, &fsinfo) => 0;
472    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
473
474    // write half COUNT files
475    prng = 42;
476    for (lfs_size_t i = 0; i < COUNT; i++) {
477        // skip half but keep our prng reproducible
478        for (lfs_size_t j = 0; j < SIZE/2; j++) {
479            TEST_PRNG(&prng);
480        }
481
482        // write the other half
483        lfs_file_t file;
484        char name[8];
485        sprintf(name, "file%03d", i);
486        lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
487        lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
488
489        for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
490            uint8_t chunk[CHUNK];
491            for (lfs_size_t k = 0; k < CHUNK; k++) {
492                chunk[k] = TEST_PRNG(&prng) & 0xff;
493            }
494
495            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
496        }
497        lfs_file_close(&lfs, &file) => 0;
498    }
499
500    // can we list the files?
501    lfs_dir_t dir;
502    lfs_dir_open(&lfs, &dir, "/") => 0;
503    struct lfs_info info;
504    lfs_dir_read(&lfs, &dir, &info) => 1;
505    assert(info.type == LFS_TYPE_DIR);
506    assert(strcmp(info.name, ".") == 0);
507    lfs_dir_read(&lfs, &dir, &info) => 1;
508    assert(info.type == LFS_TYPE_DIR);
509    assert(strcmp(info.name, "..") == 0);
510
511    for (lfs_size_t i = 0; i < COUNT; i++) {
512        lfs_dir_read(&lfs, &dir, &info) => 1;
513        assert(info.type == LFS_TYPE_REG);
514        char name[8];
515        sprintf(name, "file%03d", i);
516        assert(strcmp(info.name, name) == 0);
517        assert(info.size == SIZE);
518    }
519
520    lfs_dir_read(&lfs, &dir, &info) => 0;
521
522    // now can we read the files?
523    prng = 42;
524    for (lfs_size_t i = 0; i < COUNT; i++) {
525        lfs_file_t file;
526        char name[8];
527        sprintf(name, "file%03d", i);
528        lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
529        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
530            uint8_t chunk[CHUNK];
531            lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
532
533            for (lfs_size_t k = 0; k < CHUNK; k++) {
534                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
535            }
536        }
537        lfs_file_close(&lfs, &file) => 0;
538    }
539
540    lfs_unmount(&lfs) => 0;
541'''
542
543# test we can write files in dirs in a new version
544[cases.test_compat_forward_write_files_in_dirs]
545defines.COUNT = 5
546defines.SIZE = [4, 32, 512, 8192]
547defines.CHUNK = 2
548if = '''
549    LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR
550        && DISK_VERSION == 0
551'''
552code = '''
553    // create the previous version
554    struct lfsp_config cfgp;
555    memcpy(&cfgp, cfg, sizeof(cfgp));
556    lfsp_t lfsp;
557    lfsp_format(&lfsp, &cfgp) => 0;
558
559    // write half COUNT files
560    lfsp_mount(&lfsp, &cfgp) => 0;
561    uint32_t prng = 42;
562    for (lfs_size_t i = 0; i < COUNT; i++) {
563        char name[16];
564        sprintf(name, "dir%03d", i);
565        lfsp_mkdir(&lfsp, name) => 0;
566
567        // write half
568        lfsp_file_t file;
569        sprintf(name, "dir%03d/file%03d", i, i);
570        lfsp_file_open(&lfsp, &file, name,
571                LFSP_O_WRONLY | LFSP_O_CREAT | LFSP_O_EXCL) => 0;
572        for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
573            uint8_t chunk[CHUNK];
574            for (lfs_size_t k = 0; k < CHUNK; k++) {
575                chunk[k] = TEST_PRNG(&prng) & 0xff;
576            }
577
578            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
579        }
580        lfsp_file_close(&lfsp, &file) => 0;
581
582        // skip the other half but keep our prng reproducible
583        for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
584            TEST_PRNG(&prng);
585        }
586    }
587    lfsp_unmount(&lfsp) => 0;
588
589
590    // mount the new version
591    lfs_t lfs;
592    lfs_mount(&lfs, cfg) => 0;
593
594    // we should be able to read the version using lfs_fs_stat
595    struct lfs_fsinfo fsinfo;
596    lfs_fs_stat(&lfs, &fsinfo) => 0;
597    assert(fsinfo.disk_version == LFSP_DISK_VERSION);
598
599    // write half COUNT files
600    prng = 42;
601    for (lfs_size_t i = 0; i < COUNT; i++) {
602        // skip half but keep our prng reproducible
603        for (lfs_size_t j = 0; j < SIZE/2; j++) {
604            TEST_PRNG(&prng);
605        }
606
607        // write the other half
608        lfs_file_t file;
609        char name[16];
610        sprintf(name, "dir%03d/file%03d", i, i);
611        lfs_file_open(&lfs, &file, name, LFS_O_WRONLY) => 0;
612        lfs_file_seek(&lfs, &file, SIZE/2, LFS_SEEK_SET) => SIZE/2;
613
614        for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
615            uint8_t chunk[CHUNK];
616            for (lfs_size_t k = 0; k < CHUNK; k++) {
617                chunk[k] = TEST_PRNG(&prng) & 0xff;
618            }
619
620            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
621        }
622        lfs_file_close(&lfs, &file) => 0;
623    }
624
625    // can we list the directories?
626    lfs_dir_t dir;
627    lfs_dir_open(&lfs, &dir, "/") => 0;
628    struct lfs_info info;
629    lfs_dir_read(&lfs, &dir, &info) => 1;
630    assert(info.type == LFS_TYPE_DIR);
631    assert(strcmp(info.name, ".") == 0);
632    lfs_dir_read(&lfs, &dir, &info) => 1;
633    assert(info.type == LFS_TYPE_DIR);
634    assert(strcmp(info.name, "..") == 0);
635
636    for (lfs_size_t i = 0; i < COUNT; i++) {
637        lfs_dir_read(&lfs, &dir, &info) => 1;
638        assert(info.type == LFS_TYPE_DIR);
639        char name[8];
640        sprintf(name, "dir%03d", i);
641        assert(strcmp(info.name, name) == 0);
642    }
643
644    lfs_dir_read(&lfs, &dir, &info) => 0;
645    lfs_dir_close(&lfs, &dir) => 0;
646
647    // can we list the files?
648    for (lfs_size_t i = 0; i < COUNT; i++) {
649        char name[8];
650        sprintf(name, "dir%03d", i);
651        lfs_dir_t dir;
652        lfs_dir_open(&lfs, &dir, name) => 0;
653        struct lfs_info info;
654        lfs_dir_read(&lfs, &dir, &info) => 1;
655        assert(info.type == LFS_TYPE_DIR);
656        assert(strcmp(info.name, ".") == 0);
657        lfs_dir_read(&lfs, &dir, &info) => 1;
658        assert(info.type == LFS_TYPE_DIR);
659        assert(strcmp(info.name, "..") == 0);
660
661        lfs_dir_read(&lfs, &dir, &info) => 1;
662        assert(info.type == LFS_TYPE_REG);
663        sprintf(name, "file%03d", i);
664        assert(strcmp(info.name, name) == 0);
665        assert(info.size == SIZE);
666
667        lfs_dir_read(&lfs, &dir, &info) => 0;
668        lfs_dir_close(&lfs, &dir) => 0;
669    }
670
671    // now can we read the files?
672    prng = 42;
673    for (lfs_size_t i = 0; i < COUNT; i++) {
674        lfs_file_t file;
675        char name[16];
676        sprintf(name, "dir%03d/file%03d", i, i);
677        lfs_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
678        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
679            uint8_t chunk[CHUNK];
680            lfs_file_read(&lfs, &file, chunk, CHUNK) => CHUNK;
681
682            for (lfs_size_t k = 0; k < CHUNK; k++) {
683                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
684            }
685        }
686        lfs_file_close(&lfs, &file) => 0;
687    }
688
689    lfs_unmount(&lfs) => 0;
690'''
691
692
693
694## backwards-compatibility tests ##
695
696# test we can mount in an old version
697[cases.test_compat_backward_mount]
698if = '''
699    LFS_DISK_VERSION == LFSP_DISK_VERSION
700        && DISK_VERSION == 0
701'''
702code = '''
703    // create the new version
704    lfs_t lfs;
705    lfs_format(&lfs, cfg) => 0;
706
707    // confirm the new mount works
708    lfs_mount(&lfs, cfg) => 0;
709    lfs_unmount(&lfs) => 0;
710
711    // now test the previous mount
712    struct lfsp_config cfgp;
713    memcpy(&cfgp, cfg, sizeof(cfgp));
714    lfsp_t lfsp;
715    lfsp_mount(&lfsp, &cfgp) => 0;
716
717    lfsp_unmount(&lfsp) => 0;
718'''
719
720# test we can read dirs in an old version
721[cases.test_compat_backward_read_dirs]
722defines.COUNT = 5
723if = '''
724    LFS_DISK_VERSION == LFSP_DISK_VERSION
725        && DISK_VERSION == 0
726'''
727code = '''
728    // create the new version
729    lfs_t lfs;
730    lfs_format(&lfs, cfg) => 0;
731
732    // write COUNT dirs
733    lfs_mount(&lfs, cfg) => 0;
734    for (lfs_size_t i = 0; i < COUNT; i++) {
735        char name[8];
736        sprintf(name, "dir%03d", i);
737        lfs_mkdir(&lfs, name) => 0;
738    }
739    lfs_unmount(&lfs) => 0;
740
741
742    // mount the new version
743    struct lfsp_config cfgp;
744    memcpy(&cfgp, cfg, sizeof(cfgp));
745    lfsp_t lfsp;
746    lfsp_mount(&lfsp, &cfgp) => 0;
747
748    // can we list the directories?
749    lfsp_dir_t dir;
750    lfsp_dir_open(&lfsp, &dir, "/") => 0;
751    struct lfsp_info info;
752    lfsp_dir_read(&lfsp, &dir, &info) => 1;
753    assert(info.type == LFSP_TYPE_DIR);
754    assert(strcmp(info.name, ".") == 0);
755    lfsp_dir_read(&lfsp, &dir, &info) => 1;
756    assert(info.type == LFSP_TYPE_DIR);
757    assert(strcmp(info.name, "..") == 0);
758
759    for (lfs_size_t i = 0; i < COUNT; i++) {
760        lfsp_dir_read(&lfsp, &dir, &info) => 1;
761        assert(info.type == LFSP_TYPE_DIR);
762        char name[8];
763        sprintf(name, "dir%03d", i);
764        assert(strcmp(info.name, name) == 0);
765    }
766
767    lfsp_dir_read(&lfsp, &dir, &info) => 0;
768    lfsp_dir_close(&lfsp, &dir) => 0;
769
770    lfsp_unmount(&lfsp) => 0;
771'''
772
773# test we can read files in an old version
774[cases.test_compat_backward_read_files]
775defines.COUNT = 5
776defines.SIZE = [4, 32, 512, 8192]
777defines.CHUNK = 4
778if = '''
779    LFS_DISK_VERSION == LFSP_DISK_VERSION
780        && DISK_VERSION == 0
781'''
782code = '''
783    // create the new version
784    lfs_t lfs;
785    lfs_format(&lfs, cfg) => 0;
786
787    // write COUNT files
788    lfs_mount(&lfs, cfg) => 0;
789    uint32_t prng = 42;
790    for (lfs_size_t i = 0; i < COUNT; i++) {
791        lfs_file_t file;
792        char name[8];
793        sprintf(name, "file%03d", i);
794        lfs_file_open(&lfs, &file, name,
795                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
796        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
797            uint8_t chunk[CHUNK];
798            for (lfs_size_t k = 0; k < CHUNK; k++) {
799                chunk[k] = TEST_PRNG(&prng) & 0xff;
800            }
801
802            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
803        }
804        lfs_file_close(&lfs, &file) => 0;
805    }
806    lfs_unmount(&lfs) => 0;
807
808
809    // mount the previous version
810    struct lfsp_config cfgp;
811    memcpy(&cfgp, cfg, sizeof(cfgp));
812    lfsp_t lfsp;
813    lfsp_mount(&lfsp, &cfgp) => 0;
814
815    // can we list the files?
816    lfsp_dir_t dir;
817    lfsp_dir_open(&lfsp, &dir, "/") => 0;
818    struct lfsp_info info;
819    lfsp_dir_read(&lfsp, &dir, &info) => 1;
820    assert(info.type == LFSP_TYPE_DIR);
821    assert(strcmp(info.name, ".") == 0);
822    lfsp_dir_read(&lfsp, &dir, &info) => 1;
823    assert(info.type == LFSP_TYPE_DIR);
824    assert(strcmp(info.name, "..") == 0);
825
826    for (lfs_size_t i = 0; i < COUNT; i++) {
827        lfsp_dir_read(&lfsp, &dir, &info) => 1;
828        assert(info.type == LFSP_TYPE_REG);
829        char name[8];
830        sprintf(name, "file%03d", i);
831        assert(strcmp(info.name, name) == 0);
832        assert(info.size == SIZE);
833    }
834
835    lfsp_dir_read(&lfsp, &dir, &info) => 0;
836
837    // now can we read the files?
838    prng = 42;
839    for (lfs_size_t i = 0; i < COUNT; i++) {
840        lfsp_file_t file;
841        char name[8];
842        sprintf(name, "file%03d", i);
843        lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
844        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
845            uint8_t chunk[CHUNK];
846            lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
847
848            for (lfs_size_t k = 0; k < CHUNK; k++) {
849                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
850            }
851        }
852        lfsp_file_close(&lfsp, &file) => 0;
853    }
854
855    lfsp_unmount(&lfsp) => 0;
856'''
857
858# test we can read files in dirs in an old version
859[cases.test_compat_backward_read_files_in_dirs]
860defines.COUNT = 5
861defines.SIZE = [4, 32, 512, 8192]
862defines.CHUNK = 4
863if = '''
864    LFS_DISK_VERSION == LFSP_DISK_VERSION
865        && DISK_VERSION == 0
866'''
867code = '''
868    // create the new version
869    lfs_t lfs;
870    lfs_format(&lfs, cfg) => 0;
871
872    // write COUNT files+dirs
873    lfs_mount(&lfs, cfg) => 0;
874    uint32_t prng = 42;
875    for (lfs_size_t i = 0; i < COUNT; i++) {
876        char name[16];
877        sprintf(name, "dir%03d", i);
878        lfs_mkdir(&lfs, name) => 0;
879
880        lfs_file_t file;
881        sprintf(name, "dir%03d/file%03d", i, i);
882        lfs_file_open(&lfs, &file, name,
883                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
884        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
885            uint8_t chunk[CHUNK];
886            for (lfs_size_t k = 0; k < CHUNK; k++) {
887                chunk[k] = TEST_PRNG(&prng) & 0xff;
888            }
889
890            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
891        }
892        lfs_file_close(&lfs, &file) => 0;
893    }
894    lfs_unmount(&lfs) => 0;
895
896
897    // mount the previous version
898    struct lfsp_config cfgp;
899    memcpy(&cfgp, cfg, sizeof(cfgp));
900    lfsp_t lfsp;
901    lfsp_mount(&lfsp, &cfgp) => 0;
902
903    // can we list the directories?
904    lfsp_dir_t dir;
905    lfsp_dir_open(&lfsp, &dir, "/") => 0;
906    struct lfsp_info info;
907    lfsp_dir_read(&lfsp, &dir, &info) => 1;
908    assert(info.type == LFSP_TYPE_DIR);
909    assert(strcmp(info.name, ".") == 0);
910    lfsp_dir_read(&lfsp, &dir, &info) => 1;
911    assert(info.type == LFSP_TYPE_DIR);
912    assert(strcmp(info.name, "..") == 0);
913
914    for (lfs_size_t i = 0; i < COUNT; i++) {
915        lfsp_dir_read(&lfsp, &dir, &info) => 1;
916        assert(info.type == LFSP_TYPE_DIR);
917        char name[8];
918        sprintf(name, "dir%03d", i);
919        assert(strcmp(info.name, name) == 0);
920    }
921
922    lfsp_dir_read(&lfsp, &dir, &info) => 0;
923    lfsp_dir_close(&lfsp, &dir) => 0;
924
925    // can we list the files?
926    for (lfs_size_t i = 0; i < COUNT; i++) {
927        char name[8];
928        sprintf(name, "dir%03d", i);
929        lfsp_dir_t dir;
930        lfsp_dir_open(&lfsp, &dir, name) => 0;
931        struct lfsp_info info;
932        lfsp_dir_read(&lfsp, &dir, &info) => 1;
933        assert(info.type == LFSP_TYPE_DIR);
934        assert(strcmp(info.name, ".") == 0);
935        lfsp_dir_read(&lfsp, &dir, &info) => 1;
936        assert(info.type == LFSP_TYPE_DIR);
937        assert(strcmp(info.name, "..") == 0);
938
939        lfsp_dir_read(&lfsp, &dir, &info) => 1;
940        assert(info.type == LFSP_TYPE_REG);
941        sprintf(name, "file%03d", i);
942        assert(strcmp(info.name, name) == 0);
943        assert(info.size == SIZE);
944
945        lfsp_dir_read(&lfsp, &dir, &info) => 0;
946        lfsp_dir_close(&lfsp, &dir) => 0;
947    }
948
949    // now can we read the files?
950    prng = 42;
951    for (lfs_size_t i = 0; i < COUNT; i++) {
952        lfsp_file_t file;
953        char name[16];
954        sprintf(name, "dir%03d/file%03d", i, i);
955        lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
956        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
957            uint8_t chunk[CHUNK];
958            lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
959
960            for (lfs_size_t k = 0; k < CHUNK; k++) {
961                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
962            }
963        }
964        lfsp_file_close(&lfsp, &file) => 0;
965    }
966
967    lfsp_unmount(&lfsp) => 0;
968'''
969
970# test we can write dirs in an old version
971[cases.test_compat_backward_write_dirs]
972defines.COUNT = 10
973if = '''
974    LFS_DISK_VERSION == LFSP_DISK_VERSION
975        && DISK_VERSION == 0
976'''
977code = '''
978    // create the new version
979    lfs_t lfs;
980    lfs_format(&lfs, cfg) => 0;
981
982    // write COUNT/2 dirs
983    lfs_mount(&lfs, cfg) => 0;
984    for (lfs_size_t i = 0; i < COUNT/2; i++) {
985        char name[8];
986        sprintf(name, "dir%03d", i);
987        lfs_mkdir(&lfs, name) => 0;
988    }
989    lfs_unmount(&lfs) => 0;
990
991
992    // mount the previous version
993    struct lfsp_config cfgp;
994    memcpy(&cfgp, cfg, sizeof(cfgp));
995    lfsp_t lfsp;
996    lfsp_mount(&lfsp, &cfgp) => 0;
997
998    // write another COUNT/2 dirs
999    for (lfs_size_t i = COUNT/2; i < COUNT; i++) {
1000        char name[8];
1001        sprintf(name, "dir%03d", i);
1002        lfsp_mkdir(&lfsp, name) => 0;
1003    }
1004
1005    // can we list the directories?
1006    lfsp_dir_t dir;
1007    lfsp_dir_open(&lfsp, &dir, "/") => 0;
1008    struct lfsp_info info;
1009    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1010    assert(info.type == LFSP_TYPE_DIR);
1011    assert(strcmp(info.name, ".") == 0);
1012    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1013    assert(info.type == LFSP_TYPE_DIR);
1014    assert(strcmp(info.name, "..") == 0);
1015
1016    for (lfs_size_t i = 0; i < COUNT; i++) {
1017        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1018        assert(info.type == LFSP_TYPE_DIR);
1019        char name[8];
1020        sprintf(name, "dir%03d", i);
1021        assert(strcmp(info.name, name) == 0);
1022    }
1023
1024    lfsp_dir_read(&lfsp, &dir, &info) => 0;
1025    lfsp_dir_close(&lfsp, &dir) => 0;
1026
1027    lfsp_unmount(&lfsp) => 0;
1028'''
1029
1030# test we can write files in an old version
1031[cases.test_compat_backward_write_files]
1032defines.COUNT = 5
1033defines.SIZE = [4, 32, 512, 8192]
1034defines.CHUNK = 2
1035if = '''
1036    LFS_DISK_VERSION == LFSP_DISK_VERSION
1037        && DISK_VERSION == 0
1038'''
1039code = '''
1040    // create the previous version
1041    lfs_t lfs;
1042    lfs_format(&lfs, cfg) => 0;
1043
1044    // write half COUNT files
1045    lfs_mount(&lfs, cfg) => 0;
1046    uint32_t prng = 42;
1047    for (lfs_size_t i = 0; i < COUNT; i++) {
1048        // write half
1049        lfs_file_t file;
1050        char name[8];
1051        sprintf(name, "file%03d", i);
1052        lfs_file_open(&lfs, &file, name,
1053                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
1054        for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
1055            uint8_t chunk[CHUNK];
1056            for (lfs_size_t k = 0; k < CHUNK; k++) {
1057                chunk[k] = TEST_PRNG(&prng) & 0xff;
1058            }
1059
1060            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
1061        }
1062        lfs_file_close(&lfs, &file) => 0;
1063
1064        // skip the other half but keep our prng reproducible
1065        for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
1066            TEST_PRNG(&prng);
1067        }
1068    }
1069    lfs_unmount(&lfs) => 0;
1070
1071
1072    // mount the new version
1073    struct lfsp_config cfgp;
1074    memcpy(&cfgp, cfg, sizeof(cfgp));
1075    lfsp_t lfsp;
1076    lfsp_mount(&lfsp, &cfgp) => 0;
1077
1078    // write half COUNT files
1079    prng = 42;
1080    for (lfs_size_t i = 0; i < COUNT; i++) {
1081        // skip half but keep our prng reproducible
1082        for (lfs_size_t j = 0; j < SIZE/2; j++) {
1083            TEST_PRNG(&prng);
1084        }
1085
1086        // write the other half
1087        lfsp_file_t file;
1088        char name[8];
1089        sprintf(name, "file%03d", i);
1090        lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
1091        lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
1092
1093        for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
1094            uint8_t chunk[CHUNK];
1095            for (lfs_size_t k = 0; k < CHUNK; k++) {
1096                chunk[k] = TEST_PRNG(&prng) & 0xff;
1097            }
1098
1099            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
1100        }
1101        lfsp_file_close(&lfsp, &file) => 0;
1102    }
1103
1104    // can we list the files?
1105    lfsp_dir_t dir;
1106    lfsp_dir_open(&lfsp, &dir, "/") => 0;
1107    struct lfsp_info info;
1108    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1109    assert(info.type == LFSP_TYPE_DIR);
1110    assert(strcmp(info.name, ".") == 0);
1111    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1112    assert(info.type == LFSP_TYPE_DIR);
1113    assert(strcmp(info.name, "..") == 0);
1114
1115    for (lfs_size_t i = 0; i < COUNT; i++) {
1116        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1117        assert(info.type == LFSP_TYPE_REG);
1118        char name[8];
1119        sprintf(name, "file%03d", i);
1120        assert(strcmp(info.name, name) == 0);
1121        assert(info.size == SIZE);
1122    }
1123
1124    lfsp_dir_read(&lfsp, &dir, &info) => 0;
1125
1126    // now can we read the files?
1127    prng = 42;
1128    for (lfs_size_t i = 0; i < COUNT; i++) {
1129        lfsp_file_t file;
1130        char name[8];
1131        sprintf(name, "file%03d", i);
1132        lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
1133        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
1134            uint8_t chunk[CHUNK];
1135            lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
1136
1137            for (lfs_size_t k = 0; k < CHUNK; k++) {
1138                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
1139            }
1140        }
1141        lfsp_file_close(&lfsp, &file) => 0;
1142    }
1143
1144    lfsp_unmount(&lfsp) => 0;
1145'''
1146
1147# test we can write files in dirs in an old version
1148[cases.test_compat_backward_write_files_in_dirs]
1149defines.COUNT = 5
1150defines.SIZE = [4, 32, 512, 8192]
1151defines.CHUNK = 2
1152if = '''
1153    LFS_DISK_VERSION == LFSP_DISK_VERSION
1154        && DISK_VERSION == 0
1155'''
1156code = '''
1157    // create the previous version
1158    lfs_t lfs;
1159    lfs_format(&lfs, cfg) => 0;
1160
1161    // write half COUNT files
1162    lfs_mount(&lfs, cfg) => 0;
1163    uint32_t prng = 42;
1164    for (lfs_size_t i = 0; i < COUNT; i++) {
1165        char name[16];
1166        sprintf(name, "dir%03d", i);
1167        lfs_mkdir(&lfs, name) => 0;
1168
1169        // write half
1170        lfs_file_t file;
1171        sprintf(name, "dir%03d/file%03d", i, i);
1172        lfs_file_open(&lfs, &file, name,
1173                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
1174        for (lfs_size_t j = 0; j < SIZE/2; j += CHUNK) {
1175            uint8_t chunk[CHUNK];
1176            for (lfs_size_t k = 0; k < CHUNK; k++) {
1177                chunk[k] = TEST_PRNG(&prng) & 0xff;
1178            }
1179
1180            lfs_file_write(&lfs, &file, chunk, CHUNK) => CHUNK;
1181        }
1182        lfs_file_close(&lfs, &file) => 0;
1183
1184        // skip the other half but keep our prng reproducible
1185        for (lfs_size_t j = SIZE/2; j < SIZE; j++) {
1186            TEST_PRNG(&prng);
1187        }
1188    }
1189    lfs_unmount(&lfs) => 0;
1190
1191
1192    // mount the new version
1193    struct lfsp_config cfgp;
1194    memcpy(&cfgp, cfg, sizeof(cfgp));
1195    lfsp_t lfsp;
1196    lfsp_mount(&lfsp, &cfgp) => 0;
1197
1198    // write half COUNT files
1199    prng = 42;
1200    for (lfs_size_t i = 0; i < COUNT; i++) {
1201        // skip half but keep our prng reproducible
1202        for (lfs_size_t j = 0; j < SIZE/2; j++) {
1203            TEST_PRNG(&prng);
1204        }
1205
1206        // write the other half
1207        lfsp_file_t file;
1208        char name[16];
1209        sprintf(name, "dir%03d/file%03d", i, i);
1210        lfsp_file_open(&lfsp, &file, name, LFSP_O_WRONLY) => 0;
1211        lfsp_file_seek(&lfsp, &file, SIZE/2, LFSP_SEEK_SET) => SIZE/2;
1212
1213        for (lfs_size_t j = SIZE/2; j < SIZE; j += CHUNK) {
1214            uint8_t chunk[CHUNK];
1215            for (lfs_size_t k = 0; k < CHUNK; k++) {
1216                chunk[k] = TEST_PRNG(&prng) & 0xff;
1217            }
1218
1219            lfsp_file_write(&lfsp, &file, chunk, CHUNK) => CHUNK;
1220        }
1221        lfsp_file_close(&lfsp, &file) => 0;
1222    }
1223
1224    // can we list the directories?
1225    lfsp_dir_t dir;
1226    lfsp_dir_open(&lfsp, &dir, "/") => 0;
1227    struct lfsp_info info;
1228    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1229    assert(info.type == LFSP_TYPE_DIR);
1230    assert(strcmp(info.name, ".") == 0);
1231    lfsp_dir_read(&lfsp, &dir, &info) => 1;
1232    assert(info.type == LFSP_TYPE_DIR);
1233    assert(strcmp(info.name, "..") == 0);
1234
1235    for (lfs_size_t i = 0; i < COUNT; i++) {
1236        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1237        assert(info.type == LFSP_TYPE_DIR);
1238        char name[8];
1239        sprintf(name, "dir%03d", i);
1240        assert(strcmp(info.name, name) == 0);
1241    }
1242
1243    lfsp_dir_read(&lfsp, &dir, &info) => 0;
1244    lfsp_dir_close(&lfsp, &dir) => 0;
1245
1246    // can we list the files?
1247    for (lfs_size_t i = 0; i < COUNT; i++) {
1248        char name[8];
1249        sprintf(name, "dir%03d", i);
1250        lfsp_dir_t dir;
1251        lfsp_dir_open(&lfsp, &dir, name) => 0;
1252        struct lfsp_info info;
1253        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1254        assert(info.type == LFSP_TYPE_DIR);
1255        assert(strcmp(info.name, ".") == 0);
1256        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1257        assert(info.type == LFSP_TYPE_DIR);
1258        assert(strcmp(info.name, "..") == 0);
1259
1260        lfsp_dir_read(&lfsp, &dir, &info) => 1;
1261        assert(info.type == LFSP_TYPE_REG);
1262        sprintf(name, "file%03d", i);
1263        assert(strcmp(info.name, name) == 0);
1264        assert(info.size == SIZE);
1265
1266        lfsp_dir_read(&lfsp, &dir, &info) => 0;
1267        lfsp_dir_close(&lfsp, &dir) => 0;
1268    }
1269
1270    // now can we read the files?
1271    prng = 42;
1272    for (lfs_size_t i = 0; i < COUNT; i++) {
1273        lfsp_file_t file;
1274        char name[16];
1275        sprintf(name, "dir%03d/file%03d", i, i);
1276        lfsp_file_open(&lfsp, &file, name, LFSP_O_RDONLY) => 0;
1277        for (lfs_size_t j = 0; j < SIZE; j += CHUNK) {
1278            uint8_t chunk[CHUNK];
1279            lfsp_file_read(&lfsp, &file, chunk, CHUNK) => CHUNK;
1280
1281            for (lfs_size_t k = 0; k < CHUNK; k++) {
1282                assert(chunk[k] == TEST_PRNG(&prng) & 0xff);
1283            }
1284        }
1285        lfsp_file_close(&lfsp, &file) => 0;
1286    }
1287
1288    lfsp_unmount(&lfsp) => 0;
1289'''
1290
1291
1292
1293## incompatiblity tests ##
1294
1295# test that we fail to mount after a major version bump
1296[cases.test_compat_major_incompat]
1297in = 'lfs.c'
1298code = '''
1299    // create a superblock
1300    lfs_t lfs;
1301    lfs_format(&lfs, cfg) => 0;
1302
1303    // bump the major version
1304    //
1305    // note we're messing around with internals to do this! this
1306    // is not a user API
1307    lfs_mount(&lfs, cfg) => 0;
1308    lfs_mdir_t mdir;
1309    lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
1310    lfs_superblock_t superblock = {
1311        .version     = LFS_DISK_VERSION + 0x00010000,
1312        .block_size  = lfs.cfg->block_size,
1313        .block_count = lfs.cfg->block_count,
1314        .name_max    = lfs.name_max,
1315        .file_max    = lfs.file_max,
1316        .attr_max    = lfs.attr_max,
1317    };
1318    lfs_superblock_tole32(&superblock);
1319    lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
1320            {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
1321                &superblock})) => 0;
1322    lfs_unmount(&lfs) => 0;
1323
1324    // mount should now fail
1325    lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
1326'''
1327
1328# test that we fail to mount after a minor version bump
1329[cases.test_compat_minor_incompat]
1330in = 'lfs.c'
1331code = '''
1332    // create a superblock
1333    lfs_t lfs;
1334    lfs_format(&lfs, cfg) => 0;
1335
1336    // bump the minor version
1337    //
1338    // note we're messing around with internals to do this! this
1339    // is not a user API
1340    lfs_mount(&lfs, cfg) => 0;
1341    lfs_mdir_t mdir;
1342    lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
1343    lfs_superblock_t superblock = {
1344        .version     = LFS_DISK_VERSION + 0x00000001,
1345        .block_size  = lfs.cfg->block_size,
1346        .block_count = lfs.cfg->block_count,
1347        .name_max    = lfs.name_max,
1348        .file_max    = lfs.file_max,
1349        .attr_max    = lfs.attr_max,
1350    };
1351    lfs_superblock_tole32(&superblock);
1352    lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
1353            {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
1354                &superblock})) => 0;
1355    lfs_unmount(&lfs) => 0;
1356
1357    // mount should now fail
1358    lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
1359'''
1360
1361# test that we correctly bump the minor version
1362[cases.test_compat_minor_bump]
1363in = 'lfs.c'
1364if = '''
1365    LFS_DISK_VERSION_MINOR > 0
1366        && DISK_VERSION == 0
1367'''
1368code = '''
1369    // create a superblock
1370    lfs_t lfs;
1371    lfs_format(&lfs, cfg) => 0;
1372    lfs_mount(&lfs, cfg) => 0;
1373    lfs_file_t file;
1374    lfs_file_open(&lfs, &file, "test",
1375            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
1376    lfs_file_write(&lfs, &file, "testtest", 8) => 8;
1377    lfs_file_close(&lfs, &file) => 0;
1378    lfs_unmount(&lfs) => 0;
1379
1380    // write an old minor version
1381    //
1382    // note we're messing around with internals to do this! this
1383    // is not a user API
1384    lfs_mount(&lfs, cfg) => 0;
1385    lfs_mdir_t mdir;
1386    lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
1387    lfs_superblock_t superblock = {
1388        .version     = LFS_DISK_VERSION - 0x00000001,
1389        .block_size  = lfs.cfg->block_size,
1390        .block_count = lfs.cfg->block_count,
1391        .name_max    = lfs.name_max,
1392        .file_max    = lfs.file_max,
1393        .attr_max    = lfs.attr_max,
1394    };
1395    lfs_superblock_tole32(&superblock);
1396    lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
1397            {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
1398                &superblock})) => 0;
1399    lfs_unmount(&lfs) => 0;
1400
1401    // mount should still work
1402    lfs_mount(&lfs, cfg) => 0;
1403
1404    struct lfs_fsinfo fsinfo;
1405    lfs_fs_stat(&lfs, &fsinfo) => 0;
1406    assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
1407
1408    lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
1409    uint8_t buffer[8];
1410    lfs_file_read(&lfs, &file, buffer, 8) => 8;
1411    assert(memcmp(buffer, "testtest", 8) == 0);
1412    lfs_file_close(&lfs, &file) => 0;
1413
1414    // minor version should be unchanged
1415    lfs_fs_stat(&lfs, &fsinfo) => 0;
1416    assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
1417
1418    lfs_unmount(&lfs) => 0;
1419
1420    // if we write, we need to bump the minor version
1421    lfs_mount(&lfs, cfg) => 0;
1422
1423    lfs_fs_stat(&lfs, &fsinfo) => 0;
1424    assert(fsinfo.disk_version == LFS_DISK_VERSION-1);
1425
1426    lfs_file_open(&lfs, &file, "test", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1427    lfs_file_write(&lfs, &file, "teeeeest", 8) => 8;
1428    lfs_file_close(&lfs, &file) => 0;
1429
1430    // minor version should be changed
1431    lfs_fs_stat(&lfs, &fsinfo) => 0;
1432    assert(fsinfo.disk_version == LFS_DISK_VERSION);
1433
1434    lfs_unmount(&lfs) => 0;
1435
1436    // and of course mount should still work
1437    lfs_mount(&lfs, cfg) => 0;
1438
1439    // minor version should have changed
1440    lfs_fs_stat(&lfs, &fsinfo) => 0;
1441    assert(fsinfo.disk_version == LFS_DISK_VERSION);
1442
1443    lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0;
1444    lfs_file_read(&lfs, &file, buffer, 8) => 8;
1445    assert(memcmp(buffer, "teeeeest", 8) == 0);
1446    lfs_file_close(&lfs, &file) => 0;
1447
1448    // yep, still changed
1449    lfs_fs_stat(&lfs, &fsinfo) => 0;
1450    assert(fsinfo.disk_version == LFS_DISK_VERSION);
1451
1452    lfs_unmount(&lfs) => 0;
1453'''
1454