1# allocator tests
2# note for these to work there are a number constraints on the device geometry
3if = 'BLOCK_CYCLES == -1'
4
5# parallel allocation test
6[cases.test_alloc_parallel]
7defines.FILES = 3
8defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
9defines.GC = [false, true]
10code = '''
11    const char *names[] = {"bacon", "eggs", "pancakes"};
12    lfs_file_t files[FILES];
13
14    lfs_t lfs;
15    lfs_format(&lfs, cfg) => 0;
16    lfs_mount(&lfs, cfg) => 0;
17    lfs_mkdir(&lfs, "breakfast") => 0;
18    lfs_unmount(&lfs) => 0;
19
20    lfs_mount(&lfs, cfg) => 0;
21    for (int n = 0; n < FILES; n++) {
22        char path[1024];
23        sprintf(path, "breakfast/%s", names[n]);
24        lfs_file_open(&lfs, &files[n], path,
25                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
26    }
27    for (int n = 0; n < FILES; n++) {
28        if (GC) {
29            lfs_fs_gc(&lfs) => 0;
30        }
31        size_t size = strlen(names[n]);
32        for (lfs_size_t i = 0; i < SIZE; i += size) {
33            lfs_file_write(&lfs, &files[n], names[n], size) => size;
34        }
35    }
36    for (int n = 0; n < FILES; n++) {
37        lfs_file_close(&lfs, &files[n]) => 0;
38    }
39    lfs_unmount(&lfs) => 0;
40
41    lfs_mount(&lfs, cfg) => 0;
42    for (int n = 0; n < FILES; n++) {
43        char path[1024];
44        sprintf(path, "breakfast/%s", names[n]);
45        lfs_file_t file;
46        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
47        size_t size = strlen(names[n]);
48        for (lfs_size_t i = 0; i < SIZE; i += size) {
49            uint8_t buffer[1024];
50            lfs_file_read(&lfs, &file, buffer, size) => size;
51            assert(memcmp(buffer, names[n], size) == 0);
52        }
53        lfs_file_close(&lfs, &file) => 0;
54    }
55    lfs_unmount(&lfs) => 0;
56'''
57
58# serial allocation test
59[cases.test_alloc_serial]
60defines.FILES = 3
61defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
62defines.GC = [false, true]
63code = '''
64    const char *names[] = {"bacon", "eggs", "pancakes"};
65
66    lfs_t lfs;
67    lfs_format(&lfs, cfg) => 0;
68    lfs_mount(&lfs, cfg) => 0;
69    lfs_mkdir(&lfs, "breakfast") => 0;
70    lfs_unmount(&lfs) => 0;
71
72    for (int n = 0; n < FILES; n++) {
73        lfs_mount(&lfs, cfg) => 0;
74        char path[1024];
75        sprintf(path, "breakfast/%s", names[n]);
76        lfs_file_t file;
77        lfs_file_open(&lfs, &file, path,
78                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
79        size_t size = strlen(names[n]);
80        uint8_t buffer[1024];
81        memcpy(buffer, names[n], size);
82        for (int i = 0; i < SIZE; i += size) {
83            if (GC) {
84                lfs_fs_gc(&lfs) => 0;
85            }
86            lfs_file_write(&lfs, &file, buffer, size) => size;
87        }
88        lfs_file_close(&lfs, &file) => 0;
89        lfs_unmount(&lfs) => 0;
90    }
91
92    lfs_mount(&lfs, cfg) => 0;
93    for (int n = 0; n < FILES; n++) {
94        char path[1024];
95        sprintf(path, "breakfast/%s", names[n]);
96        lfs_file_t file;
97        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
98        size_t size = strlen(names[n]);
99        for (int i = 0; i < SIZE; i += size) {
100            uint8_t buffer[1024];
101            lfs_file_read(&lfs, &file, buffer, size) => size;
102            assert(memcmp(buffer, names[n], size) == 0);
103        }
104        lfs_file_close(&lfs, &file) => 0;
105    }
106    lfs_unmount(&lfs) => 0;
107'''
108
109# parallel allocation reuse test
110[cases.test_alloc_parallel_reuse]
111defines.FILES = 3
112defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
113defines.CYCLES = [1, 10]
114code = '''
115    const char *names[] = {"bacon", "eggs", "pancakes"};
116    lfs_file_t files[FILES];
117
118    lfs_t lfs;
119    lfs_format(&lfs, cfg) => 0;
120
121    for (int c = 0; c < CYCLES; c++) {
122        lfs_mount(&lfs, cfg) => 0;
123        lfs_mkdir(&lfs, "breakfast") => 0;
124        lfs_unmount(&lfs) => 0;
125
126        lfs_mount(&lfs, cfg) => 0;
127        for (int n = 0; n < FILES; n++) {
128            char path[1024];
129            sprintf(path, "breakfast/%s", names[n]);
130            lfs_file_open(&lfs, &files[n], path,
131                    LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
132        }
133        for (int n = 0; n < FILES; n++) {
134            size_t size = strlen(names[n]);
135            for (int i = 0; i < SIZE; i += size) {
136                lfs_file_write(&lfs, &files[n], names[n], size) => size;
137            }
138        }
139        for (int n = 0; n < FILES; n++) {
140            lfs_file_close(&lfs, &files[n]) => 0;
141        }
142        lfs_unmount(&lfs) => 0;
143
144        lfs_mount(&lfs, cfg) => 0;
145        for (int n = 0; n < FILES; n++) {
146            char path[1024];
147            sprintf(path, "breakfast/%s", names[n]);
148            lfs_file_t file;
149            lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
150            size_t size = strlen(names[n]);
151            for (int i = 0; i < SIZE; i += size) {
152                uint8_t buffer[1024];
153                lfs_file_read(&lfs, &file, buffer, size) => size;
154                assert(memcmp(buffer, names[n], size) == 0);
155            }
156            lfs_file_close(&lfs, &file) => 0;
157        }
158        lfs_unmount(&lfs) => 0;
159
160        lfs_mount(&lfs, cfg) => 0;
161        for (int n = 0; n < FILES; n++) {
162            char path[1024];
163            sprintf(path, "breakfast/%s", names[n]);
164            lfs_remove(&lfs, path) => 0;
165        }
166        lfs_remove(&lfs, "breakfast") => 0;
167        lfs_unmount(&lfs) => 0;
168    }
169'''
170
171# serial allocation reuse test
172[cases.test_alloc_serial_reuse]
173defines.FILES = 3
174defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
175defines.CYCLES = [1, 10]
176code = '''
177    const char *names[] = {"bacon", "eggs", "pancakes"};
178
179    lfs_t lfs;
180    lfs_format(&lfs, cfg) => 0;
181
182    for (int c = 0; c < CYCLES; c++) {
183        lfs_mount(&lfs, cfg) => 0;
184        lfs_mkdir(&lfs, "breakfast") => 0;
185        lfs_unmount(&lfs) => 0;
186
187        for (int n = 0; n < FILES; n++) {
188            lfs_mount(&lfs, cfg) => 0;
189            char path[1024];
190            sprintf(path, "breakfast/%s", names[n]);
191            lfs_file_t file;
192            lfs_file_open(&lfs, &file, path,
193                    LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
194            size_t size = strlen(names[n]);
195            uint8_t buffer[1024];
196            memcpy(buffer, names[n], size);
197            for (int i = 0; i < SIZE; i += size) {
198                lfs_file_write(&lfs, &file, buffer, size) => size;
199            }
200            lfs_file_close(&lfs, &file) => 0;
201            lfs_unmount(&lfs) => 0;
202        }
203
204        lfs_mount(&lfs, cfg) => 0;
205        for (int n = 0; n < FILES; n++) {
206            char path[1024];
207            sprintf(path, "breakfast/%s", names[n]);
208            lfs_file_t file;
209            lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
210            size_t size = strlen(names[n]);
211            for (int i = 0; i < SIZE; i += size) {
212                uint8_t buffer[1024];
213                lfs_file_read(&lfs, &file, buffer, size) => size;
214                assert(memcmp(buffer, names[n], size) == 0);
215            }
216            lfs_file_close(&lfs, &file) => 0;
217        }
218        lfs_unmount(&lfs) => 0;
219
220        lfs_mount(&lfs, cfg) => 0;
221        for (int n = 0; n < FILES; n++) {
222            char path[1024];
223            sprintf(path, "breakfast/%s", names[n]);
224            lfs_remove(&lfs, path) => 0;
225        }
226        lfs_remove(&lfs, "breakfast") => 0;
227        lfs_unmount(&lfs) => 0;
228    }
229'''
230
231# exhaustion test
232[cases.test_alloc_exhaustion]
233code = '''
234    lfs_t lfs;
235    lfs_format(&lfs, cfg) => 0;
236    lfs_mount(&lfs, cfg) => 0;
237    lfs_file_t file;
238    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
239    size_t size = strlen("exhaustion");
240    uint8_t buffer[1024];
241    memcpy(buffer, "exhaustion", size);
242    lfs_file_write(&lfs, &file, buffer, size) => size;
243    lfs_file_sync(&lfs, &file) => 0;
244
245    size = strlen("blahblahblahblah");
246    memcpy(buffer, "blahblahblahblah", size);
247    lfs_ssize_t res;
248    while (true) {
249        res = lfs_file_write(&lfs, &file, buffer, size);
250        if (res < 0) {
251            break;
252        }
253
254        res => size;
255    }
256    res => LFS_ERR_NOSPC;
257
258    // note that lfs_fs_gc should not error here
259    lfs_fs_gc(&lfs) => 0;
260
261    lfs_file_close(&lfs, &file) => 0;
262    lfs_unmount(&lfs) => 0;
263
264    lfs_mount(&lfs, cfg) => 0;
265    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
266    size = strlen("exhaustion");
267    lfs_file_size(&lfs, &file) => size;
268    lfs_file_read(&lfs, &file, buffer, size) => size;
269    memcmp(buffer, "exhaustion", size) => 0;
270    lfs_file_close(&lfs, &file) => 0;
271    lfs_unmount(&lfs) => 0;
272'''
273
274# exhaustion wraparound test
275[cases.test_alloc_exhaustion_wraparound]
276defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-4)) / 3)'
277code = '''
278    lfs_t lfs;
279    lfs_format(&lfs, cfg) => 0;
280    lfs_mount(&lfs, cfg) => 0;
281
282    lfs_file_t file;
283    lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT);
284    size_t size = strlen("buffering");
285    uint8_t buffer[1024];
286    memcpy(buffer, "buffering", size);
287    for (int i = 0; i < SIZE; i += size) {
288        lfs_file_write(&lfs, &file, buffer, size) => size;
289    }
290    lfs_file_close(&lfs, &file) => 0;
291    lfs_remove(&lfs, "padding") => 0;
292
293    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
294    size = strlen("exhaustion");
295    memcpy(buffer, "exhaustion", size);
296    lfs_file_write(&lfs, &file, buffer, size) => size;
297    lfs_file_sync(&lfs, &file) => 0;
298
299    size = strlen("blahblahblahblah");
300    memcpy(buffer, "blahblahblahblah", size);
301    lfs_ssize_t res;
302    while (true) {
303        res = lfs_file_write(&lfs, &file, buffer, size);
304        if (res < 0) {
305            break;
306        }
307
308        res => size;
309    }
310    res => LFS_ERR_NOSPC;
311
312    // note that lfs_fs_gc should not error here
313    lfs_fs_gc(&lfs) => 0;
314
315    lfs_file_close(&lfs, &file) => 0;
316    lfs_unmount(&lfs) => 0;
317
318    lfs_mount(&lfs, cfg) => 0;
319    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
320    size = strlen("exhaustion");
321    lfs_file_size(&lfs, &file) => size;
322    lfs_file_read(&lfs, &file, buffer, size) => size;
323    memcmp(buffer, "exhaustion", size) => 0;
324    lfs_file_close(&lfs, &file) => 0;
325    lfs_remove(&lfs, "exhaustion") => 0;
326    lfs_unmount(&lfs) => 0;
327'''
328
329# dir exhaustion test
330[cases.test_alloc_dir_exhaustion]
331code = '''
332    lfs_t lfs;
333    lfs_format(&lfs, cfg) => 0;
334    lfs_mount(&lfs, cfg) => 0;
335
336    // find out max file size
337    lfs_mkdir(&lfs, "exhaustiondir") => 0;
338    size_t size = strlen("blahblahblahblah");
339    uint8_t buffer[1024];
340    memcpy(buffer, "blahblahblahblah", size);
341    lfs_file_t file;
342    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
343    int count = 0;
344    int err;
345    while (true) {
346        err = lfs_file_write(&lfs, &file, buffer, size);
347        if (err < 0) {
348            break;
349        }
350
351        count += 1;
352    }
353    err => LFS_ERR_NOSPC;
354    // note that lfs_fs_gc should not error here
355    lfs_fs_gc(&lfs) => 0;
356    lfs_file_close(&lfs, &file) => 0;
357
358    lfs_remove(&lfs, "exhaustion") => 0;
359    lfs_remove(&lfs, "exhaustiondir") => 0;
360
361    // see if dir fits with max file size
362    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
363    for (int i = 0; i < count; i++) {
364        lfs_file_write(&lfs, &file, buffer, size) => size;
365    }
366    lfs_file_close(&lfs, &file) => 0;
367
368    lfs_mkdir(&lfs, "exhaustiondir") => 0;
369    lfs_remove(&lfs, "exhaustiondir") => 0;
370    lfs_remove(&lfs, "exhaustion") => 0;
371
372    // see if dir fits with > max file size
373    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
374    for (int i = 0; i < count+1; i++) {
375        lfs_file_write(&lfs, &file, buffer, size) => size;
376    }
377    lfs_file_close(&lfs, &file) => 0;
378
379    lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
380
381    lfs_remove(&lfs, "exhaustion") => 0;
382    lfs_unmount(&lfs) => 0;
383'''
384
385# what if we have a bad block during an allocation scan?
386[cases.test_alloc_bad_blocks]
387in = "lfs.c"
388defines.ERASE_CYCLES = 0xffffffff
389defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_READERROR'
390code = '''
391    lfs_t lfs;
392    lfs_format(&lfs, cfg) => 0;
393    lfs_mount(&lfs, cfg) => 0;
394    // first fill to exhaustion to find available space
395    lfs_file_t file;
396    lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
397    uint8_t buffer[1024];
398    strcpy((char*)buffer, "waka");
399    size_t size = strlen("waka");
400    lfs_size_t filesize = 0;
401    while (true) {
402        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
403        assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
404        if (res == LFS_ERR_NOSPC) {
405            break;
406        }
407        filesize += size;
408    }
409    lfs_file_close(&lfs, &file) => 0;
410    // now fill all but a couple of blocks of the filesystem with data
411    filesize -= 3*BLOCK_SIZE;
412    lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
413    strcpy((char*)buffer, "waka");
414    size = strlen("waka");
415    for (lfs_size_t i = 0; i < filesize/size; i++) {
416        lfs_file_write(&lfs, &file, buffer, size) => size;
417    }
418    lfs_file_close(&lfs, &file) => 0;
419    // also save head of file so we can error during lookahead scan
420    lfs_block_t fileblock = file.ctz.head;
421    lfs_unmount(&lfs) => 0;
422
423    // remount to force an alloc scan
424    lfs_mount(&lfs, cfg) => 0;
425
426    // but mark the head of our file as a "bad block", this is force our
427    // scan to bail early
428    lfs_emubd_setwear(cfg, fileblock, 0xffffffff) => 0;
429    lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
430    strcpy((char*)buffer, "chomp");
431    size = strlen("chomp");
432    while (true) {
433        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
434        assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT);
435        if (res == LFS_ERR_CORRUPT) {
436            break;
437        }
438    }
439    lfs_file_close(&lfs, &file) => 0;
440
441    // now reverse the "bad block" and try to write the file again until we
442    // run out of space
443    lfs_emubd_setwear(cfg, fileblock, 0) => 0;
444    lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
445    strcpy((char*)buffer, "chomp");
446    size = strlen("chomp");
447    while (true) {
448        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
449        assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
450        if (res == LFS_ERR_NOSPC) {
451            break;
452        }
453    }
454    // note that lfs_fs_gc should not error here
455    lfs_fs_gc(&lfs) => 0;
456    lfs_file_close(&lfs, &file) => 0;
457
458    lfs_unmount(&lfs) => 0;
459
460    // check that the disk isn't hurt
461    lfs_mount(&lfs, cfg) => 0;
462    lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0;
463    strcpy((char*)buffer, "waka");
464    size = strlen("waka");
465    for (lfs_size_t i = 0; i < filesize/size; i++) {
466        uint8_t rbuffer[4];
467        lfs_file_read(&lfs, &file, rbuffer, size) => size;
468        assert(memcmp(rbuffer, buffer, size) == 0);
469    }
470    lfs_file_close(&lfs, &file) => 0;
471    lfs_unmount(&lfs) => 0;
472'''
473
474
475# Below, I don't like these tests. They're fragile and depend _heavily_
476# on the geometry of the block device. But they are valuable. Eventually they
477# should be removed and replaced with generalized tests.
478
479# chained dir exhaustion test
480[cases.test_alloc_chained_dir_exhaustion]
481if = 'ERASE_SIZE == 512'
482defines.ERASE_COUNT = 1024
483code = '''
484    lfs_t lfs;
485    lfs_format(&lfs, cfg) => 0;
486    lfs_mount(&lfs, cfg) => 0;
487
488    // find out max file size
489    lfs_mkdir(&lfs, "exhaustiondir") => 0;
490    for (int i = 0; i < 10; i++) {
491        char path[1024];
492        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
493        lfs_mkdir(&lfs, path) => 0;
494    }
495    size_t size = strlen("blahblahblahblah");
496    uint8_t buffer[1024];
497    memcpy(buffer, "blahblahblahblah", size);
498    lfs_file_t file;
499    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
500    int count = 0;
501    int err;
502    while (true) {
503        err = lfs_file_write(&lfs, &file, buffer, size);
504        if (err < 0) {
505            break;
506        }
507
508        count += 1;
509    }
510    err => LFS_ERR_NOSPC;
511    lfs_file_close(&lfs, &file) => 0;
512
513    lfs_remove(&lfs, "exhaustion") => 0;
514    lfs_remove(&lfs, "exhaustiondir") => 0;
515    for (int i = 0; i < 10; i++) {
516        char path[1024];
517        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
518        lfs_remove(&lfs, path) => 0;
519    }
520
521    // see that chained dir fails
522    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
523    for (int i = 0; i < count+1; i++) {
524        lfs_file_write(&lfs, &file, buffer, size) => size;
525    }
526    lfs_file_sync(&lfs, &file) => 0;
527
528    for (int i = 0; i < 10; i++) {
529        char path[1024];
530        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
531        lfs_mkdir(&lfs, path) => 0;
532    }
533
534    lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
535
536    // shorten file to try a second chained dir
537    while (true) {
538        err = lfs_mkdir(&lfs, "exhaustiondir");
539        if (err != LFS_ERR_NOSPC) {
540            break;
541        }
542
543        lfs_ssize_t filesize = lfs_file_size(&lfs, &file);
544        filesize > 0 => true;
545
546        lfs_file_truncate(&lfs, &file, filesize - size) => 0;
547        lfs_file_sync(&lfs, &file) => 0;
548    }
549    err => 0;
550
551    lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
552
553    lfs_file_close(&lfs, &file) => 0;
554    lfs_unmount(&lfs) => 0;
555'''
556
557# split dir test
558[cases.test_alloc_split_dir]
559if = 'ERASE_SIZE == 512'
560defines.ERASE_COUNT = 1024
561code = '''
562    lfs_t lfs;
563    lfs_format(&lfs, cfg) => 0;
564    lfs_mount(&lfs, cfg) => 0;
565
566    // create one block hole for half a directory
567    lfs_file_t file;
568    lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
569    for (lfs_size_t i = 0; i < cfg->block_size; i += 2) {
570        uint8_t buffer[1024];
571        memcpy(&buffer[i], "hi", 2);
572    }
573    uint8_t buffer[1024];
574    lfs_file_write(&lfs, &file, buffer, cfg->block_size) => cfg->block_size;
575    lfs_file_close(&lfs, &file) => 0;
576
577    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
578    size_t size = strlen("blahblahblahblah");
579    memcpy(buffer, "blahblahblahblah", size);
580    for (lfs_size_t i = 0;
581            i < (cfg->block_count-4)*(cfg->block_size-8);
582            i += size) {
583        lfs_file_write(&lfs, &file, buffer, size) => size;
584    }
585    lfs_file_close(&lfs, &file) => 0;
586
587    // remount to force reset of lookahead
588    lfs_unmount(&lfs) => 0;
589    lfs_mount(&lfs, cfg) => 0;
590
591    // open hole
592    lfs_remove(&lfs, "bump") => 0;
593
594    lfs_mkdir(&lfs, "splitdir") => 0;
595    lfs_file_open(&lfs, &file, "splitdir/bump",
596            LFS_O_WRONLY | LFS_O_CREAT) => 0;
597    for (lfs_size_t i = 0; i < cfg->block_size; i += 2) {
598        memcpy(&buffer[i], "hi", 2);
599    }
600    lfs_file_write(&lfs, &file, buffer, 2*cfg->block_size) => LFS_ERR_NOSPC;
601    lfs_file_close(&lfs, &file) => 0;
602
603    lfs_unmount(&lfs) => 0;
604'''
605
606# outdated lookahead test
607[cases.test_alloc_outdated_lookahead]
608if = 'ERASE_SIZE == 512'
609defines.ERASE_COUNT = 1024
610code = '''
611    lfs_t lfs;
612    lfs_format(&lfs, cfg) => 0;
613    lfs_mount(&lfs, cfg) => 0;
614
615    // fill completely with two files
616    lfs_file_t file;
617    lfs_file_open(&lfs, &file, "exhaustion1",
618            LFS_O_WRONLY | LFS_O_CREAT) => 0;
619    size_t size = strlen("blahblahblahblah");
620    uint8_t buffer[1024];
621    memcpy(buffer, "blahblahblahblah", size);
622    for (lfs_size_t i = 0;
623            i < ((cfg->block_count-2)/2)*(cfg->block_size-8);
624            i += size) {
625        lfs_file_write(&lfs, &file, buffer, size) => size;
626    }
627    lfs_file_close(&lfs, &file) => 0;
628
629    lfs_file_open(&lfs, &file, "exhaustion2",
630            LFS_O_WRONLY | LFS_O_CREAT) => 0;
631    size = strlen("blahblahblahblah");
632    memcpy(buffer, "blahblahblahblah", size);
633    for (lfs_size_t i = 0;
634            i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8);
635            i += size) {
636        lfs_file_write(&lfs, &file, buffer, size) => size;
637    }
638    lfs_file_close(&lfs, &file) => 0;
639
640    // remount to force reset of lookahead
641    lfs_unmount(&lfs) => 0;
642    lfs_mount(&lfs, cfg) => 0;
643
644    // rewrite one file
645    lfs_file_open(&lfs, &file, "exhaustion1",
646            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
647    lfs_file_sync(&lfs, &file) => 0;
648    size = strlen("blahblahblahblah");
649    memcpy(buffer, "blahblahblahblah", size);
650    for (lfs_size_t i = 0;
651            i < ((cfg->block_count-2)/2)*(cfg->block_size-8);
652            i += size) {
653        lfs_file_write(&lfs, &file, buffer, size) => size;
654    }
655    lfs_file_close(&lfs, &file) => 0;
656
657    // rewrite second file, this requires lookahead does not
658    // use old population
659    lfs_file_open(&lfs, &file, "exhaustion2",
660            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
661    lfs_file_sync(&lfs, &file) => 0;
662    size = strlen("blahblahblahblah");
663    memcpy(buffer, "blahblahblahblah", size);
664    for (lfs_size_t i = 0;
665            i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8);
666            i += size) {
667        lfs_file_write(&lfs, &file, buffer, size) => size;
668    }
669    lfs_file_close(&lfs, &file) => 0;
670
671    lfs_unmount(&lfs) => 0;
672'''
673
674# outdated lookahead and split dir test
675[cases.test_alloc_outdated_lookahead_split_dir]
676if = 'ERASE_SIZE == 512'
677defines.ERASE_COUNT = 1024
678code = '''
679    lfs_t lfs;
680    lfs_format(&lfs, cfg) => 0;
681    lfs_mount(&lfs, cfg) => 0;
682
683    // fill completely with two files
684    lfs_file_t file;
685    lfs_file_open(&lfs, &file, "exhaustion1",
686            LFS_O_WRONLY | LFS_O_CREAT) => 0;
687    size_t size = strlen("blahblahblahblah");
688    uint8_t buffer[1024];
689    memcpy(buffer, "blahblahblahblah", size);
690    for (lfs_size_t i = 0;
691            i < ((cfg->block_count-2)/2)*(cfg->block_size-8);
692            i += size) {
693        lfs_file_write(&lfs, &file, buffer, size) => size;
694    }
695    lfs_file_close(&lfs, &file) => 0;
696
697    lfs_file_open(&lfs, &file, "exhaustion2",
698            LFS_O_WRONLY | LFS_O_CREAT) => 0;
699    size = strlen("blahblahblahblah");
700    memcpy(buffer, "blahblahblahblah", size);
701    for (lfs_size_t i = 0;
702            i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8);
703            i += size) {
704        lfs_file_write(&lfs, &file, buffer, size) => size;
705    }
706    lfs_file_close(&lfs, &file) => 0;
707
708    // remount to force reset of lookahead
709    lfs_unmount(&lfs) => 0;
710    lfs_mount(&lfs, cfg) => 0;
711
712    // rewrite one file with a hole of one block
713    lfs_file_open(&lfs, &file, "exhaustion1",
714            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
715    lfs_file_sync(&lfs, &file) => 0;
716    size = strlen("blahblahblahblah");
717    memcpy(buffer, "blahblahblahblah", size);
718    for (lfs_size_t i = 0;
719            i < ((cfg->block_count-2)/2 - 1)*(cfg->block_size-8);
720            i += size) {
721        lfs_file_write(&lfs, &file, buffer, size) => size;
722    }
723    lfs_file_close(&lfs, &file) => 0;
724
725    // try to allocate a directory, should fail!
726    lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC;
727
728    // file should not fail
729    lfs_file_open(&lfs, &file, "notasplit",
730            LFS_O_WRONLY | LFS_O_CREAT) => 0;
731    lfs_file_write(&lfs, &file, "hi", 2) => 2;
732    lfs_file_close(&lfs, &file) => 0;
733
734    lfs_unmount(&lfs) => 0;
735'''
736