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