1[[case]] # root
2code = '''
3    lfs_format(&lfs, &cfg) => 0;
4    lfs_mount(&lfs, &cfg) => 0;
5    lfs_dir_open(&lfs, &dir, "/") => 0;
6    lfs_dir_read(&lfs, &dir, &info) => 1;
7    assert(info.type == LFS_TYPE_DIR);
8    assert(strcmp(info.name, ".") == 0);
9    lfs_dir_read(&lfs, &dir, &info) => 1;
10    assert(info.type == LFS_TYPE_DIR);
11    assert(strcmp(info.name, "..") == 0);
12    lfs_dir_read(&lfs, &dir, &info) => 0;
13    lfs_dir_close(&lfs, &dir) => 0;
14    lfs_unmount(&lfs) => 0;
15'''
16
17[[case]] # many directory creation
18define.N = 'range(0, 100, 3)'
19code = '''
20    lfs_format(&lfs, &cfg) => 0;
21
22    lfs_mount(&lfs, &cfg) => 0;
23    for (int i = 0; i < N; i++) {
24        sprintf(path, "dir%03d", i);
25        lfs_mkdir(&lfs, path) => 0;
26    }
27    lfs_unmount(&lfs) => 0;
28
29    lfs_mount(&lfs, &cfg) => 0;
30    lfs_dir_open(&lfs, &dir, "/") => 0;
31    lfs_dir_read(&lfs, &dir, &info) => 1;
32    assert(info.type == LFS_TYPE_DIR);
33    assert(strcmp(info.name, ".") == 0);
34    lfs_dir_read(&lfs, &dir, &info) => 1;
35    assert(info.type == LFS_TYPE_DIR);
36    assert(strcmp(info.name, "..") == 0);
37    for (int i = 0; i < N; i++) {
38        sprintf(path, "dir%03d", i);
39        lfs_dir_read(&lfs, &dir, &info) => 1;
40        assert(info.type == LFS_TYPE_DIR);
41        assert(strcmp(info.name, path) == 0);
42    }
43    lfs_dir_read(&lfs, &dir, &info) => 0;
44    lfs_dir_close(&lfs, &dir) => 0;
45    lfs_unmount(&lfs) => 0;
46'''
47
48[[case]] # many directory removal
49define.N = 'range(3, 100, 11)'
50code = '''
51    lfs_format(&lfs, &cfg) => 0;
52
53    lfs_mount(&lfs, &cfg) => 0;
54    for (int i = 0; i < N; i++) {
55        sprintf(path, "removeme%03d", i);
56        lfs_mkdir(&lfs, path) => 0;
57    }
58    lfs_unmount(&lfs) => 0;
59
60    lfs_mount(&lfs, &cfg) => 0;
61    lfs_dir_open(&lfs, &dir, "/") => 0;
62    lfs_dir_read(&lfs, &dir, &info) => 1;
63    assert(info.type == LFS_TYPE_DIR);
64    assert(strcmp(info.name, ".") == 0);
65    lfs_dir_read(&lfs, &dir, &info) => 1;
66    assert(info.type == LFS_TYPE_DIR);
67    assert(strcmp(info.name, "..") == 0);
68    for (int i = 0; i < N; i++) {
69        sprintf(path, "removeme%03d", i);
70        lfs_dir_read(&lfs, &dir, &info) => 1;
71        assert(info.type == LFS_TYPE_DIR);
72        assert(strcmp(info.name, path) == 0);
73    }
74    lfs_dir_read(&lfs, &dir, &info) => 0;
75    lfs_dir_close(&lfs, &dir) => 0;
76    lfs_unmount(&lfs);
77
78    lfs_mount(&lfs, &cfg) => 0;
79    for (int i = 0; i < N; i++) {
80        sprintf(path, "removeme%03d", i);
81        lfs_remove(&lfs, path) => 0;
82    }
83    lfs_unmount(&lfs);
84
85    lfs_mount(&lfs, &cfg) => 0;
86    lfs_dir_open(&lfs, &dir, "/") => 0;
87    lfs_dir_read(&lfs, &dir, &info) => 1;
88    assert(info.type == LFS_TYPE_DIR);
89    assert(strcmp(info.name, ".") == 0);
90    lfs_dir_read(&lfs, &dir, &info) => 1;
91    assert(info.type == LFS_TYPE_DIR);
92    assert(strcmp(info.name, "..") == 0);
93    lfs_dir_read(&lfs, &dir, &info) => 0;
94    lfs_dir_close(&lfs, &dir) => 0;
95    lfs_unmount(&lfs) => 0;
96'''
97
98[[case]] # many directory rename
99define.N = 'range(3, 100, 11)'
100code = '''
101    lfs_format(&lfs, &cfg) => 0;
102
103    lfs_mount(&lfs, &cfg) => 0;
104    for (int i = 0; i < N; i++) {
105        sprintf(path, "test%03d", i);
106        lfs_mkdir(&lfs, path) => 0;
107    }
108    lfs_unmount(&lfs) => 0;
109
110    lfs_mount(&lfs, &cfg) => 0;
111    lfs_dir_open(&lfs, &dir, "/") => 0;
112    lfs_dir_read(&lfs, &dir, &info) => 1;
113    assert(info.type == LFS_TYPE_DIR);
114    assert(strcmp(info.name, ".") == 0);
115    lfs_dir_read(&lfs, &dir, &info) => 1;
116    assert(info.type == LFS_TYPE_DIR);
117    assert(strcmp(info.name, "..") == 0);
118    for (int i = 0; i < N; i++) {
119        sprintf(path, "test%03d", i);
120        lfs_dir_read(&lfs, &dir, &info) => 1;
121        assert(info.type == LFS_TYPE_DIR);
122        assert(strcmp(info.name, path) == 0);
123    }
124    lfs_dir_read(&lfs, &dir, &info) => 0;
125    lfs_dir_close(&lfs, &dir) => 0;
126    lfs_unmount(&lfs);
127
128    lfs_mount(&lfs, &cfg) => 0;
129    for (int i = 0; i < N; i++) {
130        char oldpath[128];
131        char newpath[128];
132        sprintf(oldpath, "test%03d", i);
133        sprintf(newpath, "tedd%03d", i);
134        lfs_rename(&lfs, oldpath, newpath) => 0;
135    }
136    lfs_unmount(&lfs);
137
138    lfs_mount(&lfs, &cfg) => 0;
139    lfs_dir_open(&lfs, &dir, "/") => 0;
140    lfs_dir_read(&lfs, &dir, &info) => 1;
141    assert(info.type == LFS_TYPE_DIR);
142    assert(strcmp(info.name, ".") == 0);
143    lfs_dir_read(&lfs, &dir, &info) => 1;
144    assert(info.type == LFS_TYPE_DIR);
145    assert(strcmp(info.name, "..") == 0);
146    for (int i = 0; i < N; i++) {
147        sprintf(path, "tedd%03d", i);
148        lfs_dir_read(&lfs, &dir, &info) => 1;
149        assert(info.type == LFS_TYPE_DIR);
150        assert(strcmp(info.name, path) == 0);
151    }
152    lfs_dir_read(&lfs, &dir, &info) => 0;
153    lfs_dir_close(&lfs, &dir) => 0;
154    lfs_unmount(&lfs);
155'''
156
157[[case]] # reentrant many directory creation/rename/removal
158define.N = [5, 11]
159reentrant = true
160code = '''
161    err = lfs_mount(&lfs, &cfg);
162    if (err) {
163        lfs_format(&lfs, &cfg) => 0;
164        lfs_mount(&lfs, &cfg) => 0;
165    }
166
167    for (int i = 0; i < N; i++) {
168        sprintf(path, "hi%03d", i);
169        err = lfs_mkdir(&lfs, path);
170        assert(err == 0 || err == LFS_ERR_EXIST);
171    }
172
173    for (int i = 0; i < N; i++) {
174        sprintf(path, "hello%03d", i);
175        err = lfs_remove(&lfs, path);
176        assert(err == 0 || err == LFS_ERR_NOENT);
177    }
178
179    lfs_dir_open(&lfs, &dir, "/") => 0;
180    lfs_dir_read(&lfs, &dir, &info) => 1;
181    assert(info.type == LFS_TYPE_DIR);
182    assert(strcmp(info.name, ".") == 0);
183    lfs_dir_read(&lfs, &dir, &info) => 1;
184    assert(info.type == LFS_TYPE_DIR);
185    assert(strcmp(info.name, "..") == 0);
186    for (int i = 0; i < N; i++) {
187        sprintf(path, "hi%03d", i);
188        lfs_dir_read(&lfs, &dir, &info) => 1;
189        assert(info.type == LFS_TYPE_DIR);
190        assert(strcmp(info.name, path) == 0);
191    }
192    lfs_dir_read(&lfs, &dir, &info) => 0;
193    lfs_dir_close(&lfs, &dir) => 0;
194
195    for (int i = 0; i < N; i++) {
196        char oldpath[128];
197        char newpath[128];
198        sprintf(oldpath, "hi%03d", i);
199        sprintf(newpath, "hello%03d", i);
200        // YES this can overwrite an existing newpath
201        lfs_rename(&lfs, oldpath, newpath) => 0;
202    }
203
204    lfs_dir_open(&lfs, &dir, "/") => 0;
205    lfs_dir_read(&lfs, &dir, &info) => 1;
206    assert(info.type == LFS_TYPE_DIR);
207    assert(strcmp(info.name, ".") == 0);
208    lfs_dir_read(&lfs, &dir, &info) => 1;
209    assert(info.type == LFS_TYPE_DIR);
210    assert(strcmp(info.name, "..") == 0);
211    for (int i = 0; i < N; i++) {
212        sprintf(path, "hello%03d", i);
213        lfs_dir_read(&lfs, &dir, &info) => 1;
214        assert(info.type == LFS_TYPE_DIR);
215        assert(strcmp(info.name, path) == 0);
216    }
217    lfs_dir_read(&lfs, &dir, &info) => 0;
218    lfs_dir_close(&lfs, &dir) => 0;
219
220    for (int i = 0; i < N; i++) {
221        sprintf(path, "hello%03d", i);
222        lfs_remove(&lfs, path) => 0;
223    }
224
225    lfs_dir_open(&lfs, &dir, "/") => 0;
226    lfs_dir_read(&lfs, &dir, &info) => 1;
227    assert(info.type == LFS_TYPE_DIR);
228    assert(strcmp(info.name, ".") == 0);
229    lfs_dir_read(&lfs, &dir, &info) => 1;
230    assert(info.type == LFS_TYPE_DIR);
231    assert(strcmp(info.name, "..") == 0);
232    lfs_dir_read(&lfs, &dir, &info) => 0;
233    lfs_dir_close(&lfs, &dir) => 0;
234    lfs_unmount(&lfs) => 0;
235'''
236
237[[case]] # file creation
238define.N = 'range(3, 100, 11)'
239code = '''
240    lfs_format(&lfs, &cfg) => 0;
241
242    lfs_mount(&lfs, &cfg) => 0;
243    for (int i = 0; i < N; i++) {
244        sprintf(path, "file%03d", i);
245        lfs_file_open(&lfs, &file, path,
246                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
247        lfs_file_close(&lfs, &file) => 0;
248    }
249    lfs_unmount(&lfs) => 0;
250
251    lfs_mount(&lfs, &cfg) => 0;
252    lfs_dir_open(&lfs, &dir, "/") => 0;
253    lfs_dir_read(&lfs, &dir, &info) => 1;
254    assert(info.type == LFS_TYPE_DIR);
255    assert(strcmp(info.name, ".") == 0);
256    lfs_dir_read(&lfs, &dir, &info) => 1;
257    assert(info.type == LFS_TYPE_DIR);
258    assert(strcmp(info.name, "..") == 0);
259    for (int i = 0; i < N; i++) {
260        sprintf(path, "file%03d", i);
261        lfs_dir_read(&lfs, &dir, &info) => 1;
262        assert(info.type == LFS_TYPE_REG);
263        assert(strcmp(info.name, path) == 0);
264    }
265    lfs_dir_read(&lfs, &dir, &info) => 0;
266    lfs_dir_close(&lfs, &dir) => 0;
267    lfs_unmount(&lfs);
268'''
269
270[[case]] # file removal
271define.N = 'range(0, 100, 3)'
272code = '''
273    lfs_format(&lfs, &cfg) => 0;
274
275    lfs_mount(&lfs, &cfg) => 0;
276    for (int i = 0; i < N; i++) {
277        sprintf(path, "removeme%03d", i);
278        lfs_file_open(&lfs, &file, path,
279                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
280        lfs_file_close(&lfs, &file) => 0;
281    }
282    lfs_unmount(&lfs) => 0;
283
284    lfs_mount(&lfs, &cfg) => 0;
285    lfs_dir_open(&lfs, &dir, "/") => 0;
286    lfs_dir_read(&lfs, &dir, &info) => 1;
287    assert(info.type == LFS_TYPE_DIR);
288    assert(strcmp(info.name, ".") == 0);
289    lfs_dir_read(&lfs, &dir, &info) => 1;
290    assert(info.type == LFS_TYPE_DIR);
291    assert(strcmp(info.name, "..") == 0);
292    for (int i = 0; i < N; i++) {
293        sprintf(path, "removeme%03d", i);
294        lfs_dir_read(&lfs, &dir, &info) => 1;
295        assert(info.type == LFS_TYPE_REG);
296        assert(strcmp(info.name, path) == 0);
297    }
298    lfs_dir_read(&lfs, &dir, &info) => 0;
299    lfs_dir_close(&lfs, &dir) => 0;
300    lfs_unmount(&lfs);
301
302    lfs_mount(&lfs, &cfg) => 0;
303    for (int i = 0; i < N; i++) {
304        sprintf(path, "removeme%03d", i);
305        lfs_remove(&lfs, path) => 0;
306    }
307    lfs_unmount(&lfs);
308
309    lfs_mount(&lfs, &cfg) => 0;
310    lfs_dir_open(&lfs, &dir, "/") => 0;
311    lfs_dir_read(&lfs, &dir, &info) => 1;
312    assert(info.type == LFS_TYPE_DIR);
313    assert(strcmp(info.name, ".") == 0);
314    lfs_dir_read(&lfs, &dir, &info) => 1;
315    assert(info.type == LFS_TYPE_DIR);
316    assert(strcmp(info.name, "..") == 0);
317    lfs_dir_read(&lfs, &dir, &info) => 0;
318    lfs_dir_close(&lfs, &dir) => 0;
319    lfs_unmount(&lfs) => 0;
320'''
321
322[[case]] # file rename
323define.N = 'range(0, 100, 3)'
324code = '''
325    lfs_format(&lfs, &cfg) => 0;
326
327    lfs_mount(&lfs, &cfg) => 0;
328    for (int i = 0; i < N; i++) {
329        sprintf(path, "test%03d", i);
330        lfs_file_open(&lfs, &file, path,
331                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
332        lfs_file_close(&lfs, &file) => 0;
333    }
334    lfs_unmount(&lfs) => 0;
335
336    lfs_mount(&lfs, &cfg) => 0;
337    lfs_dir_open(&lfs, &dir, "/") => 0;
338    lfs_dir_read(&lfs, &dir, &info) => 1;
339    assert(info.type == LFS_TYPE_DIR);
340    assert(strcmp(info.name, ".") == 0);
341    lfs_dir_read(&lfs, &dir, &info) => 1;
342    assert(info.type == LFS_TYPE_DIR);
343    assert(strcmp(info.name, "..") == 0);
344    for (int i = 0; i < N; i++) {
345        sprintf(path, "test%03d", i);
346        lfs_dir_read(&lfs, &dir, &info) => 1;
347        assert(info.type == LFS_TYPE_REG);
348        assert(strcmp(info.name, path) == 0);
349    }
350    lfs_dir_read(&lfs, &dir, &info) => 0;
351    lfs_dir_close(&lfs, &dir) => 0;
352    lfs_unmount(&lfs);
353
354    lfs_mount(&lfs, &cfg) => 0;
355    for (int i = 0; i < N; i++) {
356        char oldpath[128];
357        char newpath[128];
358        sprintf(oldpath, "test%03d", i);
359        sprintf(newpath, "tedd%03d", i);
360        lfs_rename(&lfs, oldpath, newpath) => 0;
361    }
362    lfs_unmount(&lfs);
363
364    lfs_mount(&lfs, &cfg) => 0;
365    lfs_dir_open(&lfs, &dir, "/") => 0;
366    lfs_dir_read(&lfs, &dir, &info) => 1;
367    assert(info.type == LFS_TYPE_DIR);
368    assert(strcmp(info.name, ".") == 0);
369    lfs_dir_read(&lfs, &dir, &info) => 1;
370    assert(info.type == LFS_TYPE_DIR);
371    assert(strcmp(info.name, "..") == 0);
372    for (int i = 0; i < N; i++) {
373        sprintf(path, "tedd%03d", i);
374        lfs_dir_read(&lfs, &dir, &info) => 1;
375        assert(info.type == LFS_TYPE_REG);
376        assert(strcmp(info.name, path) == 0);
377    }
378    lfs_dir_read(&lfs, &dir, &info) => 0;
379    lfs_dir_close(&lfs, &dir) => 0;
380    lfs_unmount(&lfs);
381'''
382
383[[case]] # reentrant file creation/rename/removal
384define.N = [5, 25]
385reentrant = true
386code = '''
387    err = lfs_mount(&lfs, &cfg);
388    if (err) {
389        lfs_format(&lfs, &cfg) => 0;
390        lfs_mount(&lfs, &cfg) => 0;
391    }
392
393    for (int i = 0; i < N; i++) {
394        sprintf(path, "hi%03d", i);
395        lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
396        lfs_file_close(&lfs, &file) => 0;
397    }
398
399    for (int i = 0; i < N; i++) {
400        sprintf(path, "hello%03d", i);
401        err = lfs_remove(&lfs, path);
402        assert(err == 0 || err == LFS_ERR_NOENT);
403    }
404
405    lfs_dir_open(&lfs, &dir, "/") => 0;
406    lfs_dir_read(&lfs, &dir, &info) => 1;
407    assert(info.type == LFS_TYPE_DIR);
408    assert(strcmp(info.name, ".") == 0);
409    lfs_dir_read(&lfs, &dir, &info) => 1;
410    assert(info.type == LFS_TYPE_DIR);
411    assert(strcmp(info.name, "..") == 0);
412    for (int i = 0; i < N; i++) {
413        sprintf(path, "hi%03d", i);
414        lfs_dir_read(&lfs, &dir, &info) => 1;
415        assert(info.type == LFS_TYPE_REG);
416        assert(strcmp(info.name, path) == 0);
417    }
418    lfs_dir_read(&lfs, &dir, &info) => 0;
419    lfs_dir_close(&lfs, &dir) => 0;
420
421    for (int i = 0; i < N; i++) {
422        char oldpath[128];
423        char newpath[128];
424        sprintf(oldpath, "hi%03d", i);
425        sprintf(newpath, "hello%03d", i);
426        // YES this can overwrite an existing newpath
427        lfs_rename(&lfs, oldpath, newpath) => 0;
428    }
429
430    lfs_dir_open(&lfs, &dir, "/") => 0;
431    lfs_dir_read(&lfs, &dir, &info) => 1;
432    assert(info.type == LFS_TYPE_DIR);
433    assert(strcmp(info.name, ".") == 0);
434    lfs_dir_read(&lfs, &dir, &info) => 1;
435    assert(info.type == LFS_TYPE_DIR);
436    assert(strcmp(info.name, "..") == 0);
437    for (int i = 0; i < N; i++) {
438        sprintf(path, "hello%03d", i);
439        lfs_dir_read(&lfs, &dir, &info) => 1;
440        assert(info.type == LFS_TYPE_REG);
441        assert(strcmp(info.name, path) == 0);
442    }
443    lfs_dir_read(&lfs, &dir, &info) => 0;
444    lfs_dir_close(&lfs, &dir) => 0;
445
446    for (int i = 0; i < N; i++) {
447        sprintf(path, "hello%03d", i);
448        lfs_remove(&lfs, path) => 0;
449    }
450
451    lfs_dir_open(&lfs, &dir, "/") => 0;
452    lfs_dir_read(&lfs, &dir, &info) => 1;
453    assert(info.type == LFS_TYPE_DIR);
454    assert(strcmp(info.name, ".") == 0);
455    lfs_dir_read(&lfs, &dir, &info) => 1;
456    assert(info.type == LFS_TYPE_DIR);
457    assert(strcmp(info.name, "..") == 0);
458    lfs_dir_read(&lfs, &dir, &info) => 0;
459    lfs_dir_close(&lfs, &dir) => 0;
460    lfs_unmount(&lfs) => 0;
461'''
462
463[[case]] # nested directories
464code = '''
465    lfs_format(&lfs, &cfg) => 0;
466    lfs_mount(&lfs, &cfg) => 0;
467    lfs_mkdir(&lfs, "potato") => 0;
468    lfs_file_open(&lfs, &file, "burito",
469            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
470    lfs_file_close(&lfs, &file) => 0;
471    lfs_unmount(&lfs) => 0;
472
473    lfs_mount(&lfs, &cfg) => 0;
474    lfs_mkdir(&lfs, "potato/baked") => 0;
475    lfs_mkdir(&lfs, "potato/sweet") => 0;
476    lfs_mkdir(&lfs, "potato/fried") => 0;
477    lfs_unmount(&lfs) => 0;
478
479    lfs_mount(&lfs, &cfg) => 0;
480    lfs_dir_open(&lfs, &dir, "potato") => 0;
481    lfs_dir_read(&lfs, &dir, &info) => 1;
482    assert(strcmp(info.name, ".") == 0);
483    info.type => LFS_TYPE_DIR;
484    lfs_dir_read(&lfs, &dir, &info) => 1;
485    assert(strcmp(info.name, "..") == 0);
486    info.type => LFS_TYPE_DIR;
487    lfs_dir_read(&lfs, &dir, &info) => 1;
488    assert(strcmp(info.name, "baked") == 0);
489    info.type => LFS_TYPE_DIR;
490    lfs_dir_read(&lfs, &dir, &info) => 1;
491    assert(strcmp(info.name, "fried") == 0);
492    info.type => LFS_TYPE_DIR;
493    lfs_dir_read(&lfs, &dir, &info) => 1;
494    assert(strcmp(info.name, "sweet") == 0);
495    info.type => LFS_TYPE_DIR;
496    lfs_dir_read(&lfs, &dir, &info) => 0;
497    lfs_dir_close(&lfs, &dir) => 0;
498    lfs_unmount(&lfs) => 0;
499
500    // try removing?
501    lfs_mount(&lfs, &cfg) => 0;
502    lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY;
503    lfs_unmount(&lfs) => 0;
504
505    // try renaming?
506    lfs_mount(&lfs, &cfg) => 0;
507    lfs_rename(&lfs, "potato", "coldpotato") => 0;
508    lfs_unmount(&lfs) => 0;
509
510    lfs_mount(&lfs, &cfg) => 0;
511    lfs_rename(&lfs, "coldpotato", "warmpotato") => 0;
512    lfs_rename(&lfs, "warmpotato", "hotpotato") => 0;
513    lfs_unmount(&lfs) => 0;
514
515    lfs_mount(&lfs, &cfg) => 0;
516    lfs_remove(&lfs, "potato") => LFS_ERR_NOENT;
517    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
518    lfs_remove(&lfs, "warmpotato") => LFS_ERR_NOENT;
519    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
520    lfs_unmount(&lfs) => 0;
521
522    // try cross-directory renaming
523    lfs_mount(&lfs, &cfg) => 0;
524    lfs_mkdir(&lfs, "coldpotato") => 0;
525    lfs_rename(&lfs, "hotpotato/baked", "coldpotato/baked") => 0;
526    lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
527    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
528    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
529    lfs_rename(&lfs, "hotpotato/fried", "coldpotato/fried") => 0;
530    lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
531    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
532    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
533    lfs_rename(&lfs, "hotpotato/sweet", "coldpotato/sweet") => 0;
534    lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
535    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
536    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
537    lfs_unmount(&lfs) => 0;
538
539    lfs_mount(&lfs, &cfg) => 0;
540    lfs_dir_open(&lfs, &dir, "hotpotato") => 0;
541    lfs_dir_read(&lfs, &dir, &info) => 1;
542    assert(strcmp(info.name, ".") == 0);
543    info.type => LFS_TYPE_DIR;
544    lfs_dir_read(&lfs, &dir, &info) => 1;
545    assert(strcmp(info.name, "..") == 0);
546    info.type => LFS_TYPE_DIR;
547    lfs_dir_read(&lfs, &dir, &info) => 1;
548    assert(strcmp(info.name, "baked") == 0);
549    info.type => LFS_TYPE_DIR;
550    lfs_dir_read(&lfs, &dir, &info) => 1;
551    assert(strcmp(info.name, "fried") == 0);
552    info.type => LFS_TYPE_DIR;
553    lfs_dir_read(&lfs, &dir, &info) => 1;
554    assert(strcmp(info.name, "sweet") == 0);
555    info.type => LFS_TYPE_DIR;
556    lfs_dir_read(&lfs, &dir, &info) => 0;
557    lfs_dir_close(&lfs, &dir) => 0;
558    lfs_unmount(&lfs) => 0;
559
560    // final remove
561    lfs_mount(&lfs, &cfg) => 0;
562    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
563    lfs_remove(&lfs, "hotpotato/baked") => 0;
564    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
565    lfs_remove(&lfs, "hotpotato/fried") => 0;
566    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
567    lfs_remove(&lfs, "hotpotato/sweet") => 0;
568    lfs_remove(&lfs, "hotpotato") => 0;
569    lfs_unmount(&lfs) => 0;
570
571    lfs_mount(&lfs, &cfg) => 0;
572    lfs_dir_open(&lfs, &dir, "/") => 0;
573    lfs_dir_read(&lfs, &dir, &info) => 1;
574    assert(strcmp(info.name, ".") == 0);
575    info.type => LFS_TYPE_DIR;
576    lfs_dir_read(&lfs, &dir, &info) => 1;
577    assert(strcmp(info.name, "..") == 0);
578    info.type => LFS_TYPE_DIR;
579    lfs_dir_read(&lfs, &dir, &info) => 1;
580    assert(strcmp(info.name, "burito") == 0);
581    info.type => LFS_TYPE_REG;
582    lfs_dir_read(&lfs, &dir, &info) => 0;
583    lfs_dir_close(&lfs, &dir) => 0;
584    lfs_unmount(&lfs) => 0;
585'''
586
587[[case]] # recursive remove
588define.N = [10, 100]
589code = '''
590    lfs_format(&lfs, &cfg) => 0;
591    lfs_mount(&lfs, &cfg) => 0;
592    lfs_mkdir(&lfs, "prickly-pear") => 0;
593    for (int i = 0; i < N; i++) {
594        sprintf(path, "prickly-pear/cactus%03d", i);
595        lfs_mkdir(&lfs, path) => 0;
596    }
597    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
598    lfs_dir_read(&lfs, &dir, &info) => 1;
599    assert(info.type == LFS_TYPE_DIR);
600    assert(strcmp(info.name, ".") == 0);
601    lfs_dir_read(&lfs, &dir, &info) => 1;
602    assert(info.type == LFS_TYPE_DIR);
603    assert(strcmp(info.name, "..") == 0);
604    for (int i = 0; i < N; i++) {
605        sprintf(path, "cactus%03d", i);
606        lfs_dir_read(&lfs, &dir, &info) => 1;
607        assert(info.type == LFS_TYPE_DIR);
608        assert(strcmp(info.name, path) == 0);
609    }
610    lfs_dir_read(&lfs, &dir, &info) => 0;
611    lfs_dir_close(&lfs, &dir) => 0;
612    lfs_unmount(&lfs);
613
614    lfs_mount(&lfs, &cfg) => 0;
615    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
616
617    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
618    lfs_dir_read(&lfs, &dir, &info) => 1;
619    assert(info.type == LFS_TYPE_DIR);
620    assert(strcmp(info.name, ".") == 0);
621    lfs_dir_read(&lfs, &dir, &info) => 1;
622    assert(info.type == LFS_TYPE_DIR);
623    assert(strcmp(info.name, "..") == 0);
624    for (int i = 0; i < N; i++) {
625        sprintf(path, "cactus%03d", i);
626        lfs_dir_read(&lfs, &dir, &info) => 1;
627        assert(info.type == LFS_TYPE_DIR);
628        assert(strcmp(info.name, path) == 0);
629        sprintf(path, "prickly-pear/%s", info.name);
630        lfs_remove(&lfs, path) => 0;
631    }
632    lfs_dir_read(&lfs, &dir, &info) => 0;
633    lfs_dir_close(&lfs, &dir) => 0;
634
635    lfs_remove(&lfs, "prickly-pear") => 0;
636    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
637    lfs_unmount(&lfs) => 0;
638
639    lfs_mount(&lfs, &cfg) => 0;
640    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
641    lfs_unmount(&lfs) => 0;
642'''
643
644[[case]] # other error cases
645code = '''
646    lfs_format(&lfs, &cfg) => 0;
647    lfs_mount(&lfs, &cfg) => 0;
648    lfs_mkdir(&lfs, "potato") => 0;
649    lfs_file_open(&lfs, &file, "burito",
650            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
651    lfs_file_close(&lfs, &file) => 0;
652    lfs_unmount(&lfs) => 0;
653
654    lfs_mount(&lfs, &cfg) => 0;
655
656    lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
657    lfs_mkdir(&lfs, "burito") => LFS_ERR_EXIST;
658    lfs_file_open(&lfs, &file, "burito",
659            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
660    lfs_file_open(&lfs, &file, "potato",
661            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
662    lfs_dir_open(&lfs, &dir, "tomato") => LFS_ERR_NOENT;
663    lfs_dir_open(&lfs, &dir, "burito") => LFS_ERR_NOTDIR;
664    lfs_file_open(&lfs, &file, "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT;
665    lfs_file_open(&lfs, &file, "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR;
666    lfs_file_open(&lfs, &file, "tomato", LFS_O_WRONLY) => LFS_ERR_NOENT;
667    lfs_file_open(&lfs, &file, "potato", LFS_O_WRONLY) => LFS_ERR_ISDIR;
668    lfs_file_open(&lfs, &file, "potato",
669            LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
670
671    lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST;
672    lfs_file_open(&lfs, &file, "/",
673            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
674    lfs_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_ISDIR;
675    lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_ISDIR;
676    lfs_file_open(&lfs, &file, "/",
677            LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
678
679    // check that errors did not corrupt directory
680    lfs_dir_open(&lfs, &dir, "/") => 0;
681    lfs_dir_read(&lfs, &dir, &info) => 1;
682    assert(info.type == LFS_TYPE_DIR);
683    assert(strcmp(info.name, ".") == 0);
684    lfs_dir_read(&lfs, &dir, &info) => 1;
685    assert(info.type == LFS_TYPE_DIR);
686    assert(strcmp(info.name, "..") == 0);
687    lfs_dir_read(&lfs, &dir, &info) => 1;
688    assert(info.type == LFS_TYPE_REG);
689    assert(strcmp(info.name, "burito") == 0);
690    lfs_dir_read(&lfs, &dir, &info) => 1;
691    assert(info.type == LFS_TYPE_DIR);
692    assert(strcmp(info.name, "potato") == 0);
693    lfs_dir_read(&lfs, &dir, &info) => 0;
694    lfs_dir_close(&lfs, &dir) => 0;
695
696    lfs_unmount(&lfs) => 0;
697
698    // or on disk
699    lfs_mount(&lfs, &cfg) => 0;
700    lfs_dir_open(&lfs, &dir, "/") => 0;
701    lfs_dir_read(&lfs, &dir, &info) => 1;
702    assert(info.type == LFS_TYPE_DIR);
703    assert(strcmp(info.name, ".") == 0);
704    lfs_dir_read(&lfs, &dir, &info) => 1;
705    assert(info.type == LFS_TYPE_DIR);
706    assert(strcmp(info.name, "..") == 0);
707    lfs_dir_read(&lfs, &dir, &info) => 1;
708    assert(info.type == LFS_TYPE_REG);
709    assert(strcmp(info.name, "burito") == 0);
710    lfs_dir_read(&lfs, &dir, &info) => 1;
711    assert(info.type == LFS_TYPE_DIR);
712    assert(strcmp(info.name, "potato") == 0);
713    lfs_dir_read(&lfs, &dir, &info) => 0;
714    lfs_dir_close(&lfs, &dir) => 0;
715    lfs_unmount(&lfs) => 0;
716'''
717
718[[case]] # directory seek
719define.COUNT = [4, 128, 132]
720code = '''
721    lfs_format(&lfs, &cfg) => 0;
722    lfs_mount(&lfs, &cfg) => 0;
723    lfs_mkdir(&lfs, "hello") => 0;
724    for (int i = 0; i < COUNT; i++) {
725        sprintf(path, "hello/kitty%03d", i);
726        lfs_mkdir(&lfs, path) => 0;
727    }
728    lfs_unmount(&lfs) => 0;
729
730    for (int j = 2; j < COUNT; j++) {
731        lfs_mount(&lfs, &cfg) => 0;
732        lfs_dir_open(&lfs, &dir, "hello") => 0;
733        lfs_dir_read(&lfs, &dir, &info) => 1;
734        assert(strcmp(info.name, ".") == 0);
735        assert(info.type == LFS_TYPE_DIR);
736        lfs_dir_read(&lfs, &dir, &info) => 1;
737        assert(strcmp(info.name, "..") == 0);
738        assert(info.type == LFS_TYPE_DIR);
739
740        lfs_soff_t pos;
741        for (int i = 0; i < j; i++) {
742            sprintf(path, "kitty%03d", i);
743            lfs_dir_read(&lfs, &dir, &info) => 1;
744            assert(strcmp(info.name, path) == 0);
745            assert(info.type == LFS_TYPE_DIR);
746            pos = lfs_dir_tell(&lfs, &dir);
747            assert(pos >= 0);
748        }
749
750        lfs_dir_seek(&lfs, &dir, pos) => 0;
751        sprintf(path, "kitty%03d", j);
752        lfs_dir_read(&lfs, &dir, &info) => 1;
753        assert(strcmp(info.name, path) == 0);
754        assert(info.type == LFS_TYPE_DIR);
755
756        lfs_dir_rewind(&lfs, &dir) => 0;
757        sprintf(path, "kitty%03d", 0);
758        lfs_dir_read(&lfs, &dir, &info) => 1;
759        assert(strcmp(info.name, ".") == 0);
760        assert(info.type == LFS_TYPE_DIR);
761        lfs_dir_read(&lfs, &dir, &info) => 1;
762        assert(strcmp(info.name, "..") == 0);
763        assert(info.type == LFS_TYPE_DIR);
764        lfs_dir_read(&lfs, &dir, &info) => 1;
765        assert(strcmp(info.name, path) == 0);
766        assert(info.type == LFS_TYPE_DIR);
767
768        lfs_dir_seek(&lfs, &dir, pos) => 0;
769        sprintf(path, "kitty%03d", j);
770        lfs_dir_read(&lfs, &dir, &info) => 1;
771        assert(strcmp(info.name, path) == 0);
772        assert(info.type == LFS_TYPE_DIR);
773
774        lfs_dir_close(&lfs, &dir) => 0;
775        lfs_unmount(&lfs) => 0;
776    }
777'''
778
779[[case]] # root seek
780define.COUNT = [4, 128, 132]
781code = '''
782    lfs_format(&lfs, &cfg) => 0;
783    lfs_mount(&lfs, &cfg) => 0;
784    for (int i = 0; i < COUNT; i++) {
785        sprintf(path, "hi%03d", i);
786        lfs_mkdir(&lfs, path) => 0;
787    }
788    lfs_unmount(&lfs) => 0;
789
790    for (int j = 2; j < COUNT; j++) {
791        lfs_mount(&lfs, &cfg) => 0;
792        lfs_dir_open(&lfs, &dir, "/") => 0;
793        lfs_dir_read(&lfs, &dir, &info) => 1;
794        assert(strcmp(info.name, ".") == 0);
795        assert(info.type == LFS_TYPE_DIR);
796        lfs_dir_read(&lfs, &dir, &info) => 1;
797        assert(strcmp(info.name, "..") == 0);
798        assert(info.type == LFS_TYPE_DIR);
799
800        lfs_soff_t pos;
801        for (int i = 0; i < j; i++) {
802            sprintf(path, "hi%03d", i);
803            lfs_dir_read(&lfs, &dir, &info) => 1;
804            assert(strcmp(info.name, path) == 0);
805            assert(info.type == LFS_TYPE_DIR);
806            pos = lfs_dir_tell(&lfs, &dir);
807            assert(pos >= 0);
808        }
809
810        lfs_dir_seek(&lfs, &dir, pos) => 0;
811        sprintf(path, "hi%03d", j);
812        lfs_dir_read(&lfs, &dir, &info) => 1;
813        assert(strcmp(info.name, path) == 0);
814        assert(info.type == LFS_TYPE_DIR);
815
816        lfs_dir_rewind(&lfs, &dir) => 0;
817        sprintf(path, "hi%03d", 0);
818        lfs_dir_read(&lfs, &dir, &info) => 1;
819        assert(strcmp(info.name, ".") == 0);
820        assert(info.type == LFS_TYPE_DIR);
821        lfs_dir_read(&lfs, &dir, &info) => 1;
822        assert(strcmp(info.name, "..") == 0);
823        assert(info.type == LFS_TYPE_DIR);
824        lfs_dir_read(&lfs, &dir, &info) => 1;
825        assert(strcmp(info.name, path) == 0);
826        assert(info.type == LFS_TYPE_DIR);
827
828        lfs_dir_seek(&lfs, &dir, pos) => 0;
829        sprintf(path, "hi%03d", j);
830        lfs_dir_read(&lfs, &dir, &info) => 1;
831        assert(strcmp(info.name, path) == 0);
832        assert(info.type == LFS_TYPE_DIR);
833
834        lfs_dir_close(&lfs, &dir) => 0;
835        lfs_unmount(&lfs) => 0;
836    }
837'''
838
839