1# specific corner cases worth explicitly testing for
2[cases.test_relocations_dangling_split_dir]
3defines.ITERATIONS = 20
4defines.COUNT = 10
5defines.BLOCK_CYCLES = [8, 1]
6code = '''
7    lfs_t lfs;
8    lfs_format(&lfs, cfg) => 0;
9    // fill up filesystem so only ~16 blocks are left
10    lfs_mount(&lfs, cfg) => 0;
11    lfs_file_t file;
12    lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
13    uint8_t buffer[512];
14    memset(buffer, 0, 512);
15    while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
16        lfs_file_write(&lfs, &file, buffer, 512) => 512;
17    }
18    lfs_file_close(&lfs, &file) => 0;
19    // make a child dir to use in bounded space
20    lfs_mkdir(&lfs, "child") => 0;
21    lfs_unmount(&lfs) => 0;
22
23    lfs_mount(&lfs, cfg) => 0;
24    for (unsigned j = 0; j < ITERATIONS; j++) {
25        for (unsigned i = 0; i < COUNT; i++) {
26            char path[1024];
27            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
28            lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
29            lfs_file_close(&lfs, &file) => 0;
30        }
31
32        lfs_dir_t dir;
33        struct lfs_info info;
34        lfs_dir_open(&lfs, &dir, "child") => 0;
35        lfs_dir_read(&lfs, &dir, &info) => 1;
36        lfs_dir_read(&lfs, &dir, &info) => 1;
37        for (unsigned i = 0; i < COUNT; i++) {
38            char path[1024];
39            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
40            lfs_dir_read(&lfs, &dir, &info) => 1;
41            strcmp(info.name, path) => 0;
42        }
43        lfs_dir_read(&lfs, &dir, &info) => 0;
44        lfs_dir_close(&lfs, &dir) => 0;
45
46        if (j == (unsigned)ITERATIONS-1) {
47            break;
48        }
49
50        for (unsigned i = 0; i < COUNT; i++) {
51            char path[1024];
52            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
53            lfs_remove(&lfs, path) => 0;
54        }
55    }
56    lfs_unmount(&lfs) => 0;
57
58    lfs_mount(&lfs, cfg) => 0;
59    lfs_dir_t dir;
60    struct lfs_info info;
61    lfs_dir_open(&lfs, &dir, "child") => 0;
62    lfs_dir_read(&lfs, &dir, &info) => 1;
63    lfs_dir_read(&lfs, &dir, &info) => 1;
64    for (unsigned i = 0; i < COUNT; i++) {
65        char path[1024];
66        sprintf(path, "test%03d_loooooooooooooooooong_name", i);
67        lfs_dir_read(&lfs, &dir, &info) => 1;
68        strcmp(info.name, path) => 0;
69    }
70    lfs_dir_read(&lfs, &dir, &info) => 0;
71    lfs_dir_close(&lfs, &dir) => 0;
72    for (unsigned i = 0; i < COUNT; i++) {
73        char path[1024];
74        sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
75        lfs_remove(&lfs, path) => 0;
76    }
77    lfs_unmount(&lfs) => 0;
78'''
79
80[cases.test_relocations_outdated_head]
81defines.ITERATIONS = 20
82defines.COUNT = 10
83defines.BLOCK_CYCLES = [8, 1]
84code = '''
85    lfs_t lfs;
86    lfs_format(&lfs, cfg) => 0;
87    // fill up filesystem so only ~16 blocks are left
88    lfs_mount(&lfs, cfg) => 0;
89    lfs_file_t file;
90    lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
91    uint8_t buffer[512];
92    memset(buffer, 0, 512);
93    while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
94        lfs_file_write(&lfs, &file, buffer, 512) => 512;
95    }
96    lfs_file_close(&lfs, &file) => 0;
97    // make a child dir to use in bounded space
98    lfs_mkdir(&lfs, "child") => 0;
99    lfs_unmount(&lfs) => 0;
100
101    lfs_mount(&lfs, cfg) => 0;
102    for (unsigned j = 0; j < ITERATIONS; j++) {
103        for (unsigned i = 0; i < COUNT; i++) {
104            char path[1024];
105            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
106            lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
107            lfs_file_close(&lfs, &file) => 0;
108        }
109
110        lfs_dir_t dir;
111        struct lfs_info info;
112        lfs_dir_open(&lfs, &dir, "child") => 0;
113        lfs_dir_read(&lfs, &dir, &info) => 1;
114        lfs_dir_read(&lfs, &dir, &info) => 1;
115        for (unsigned i = 0; i < COUNT; i++) {
116            char path[1024];
117            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
118            lfs_dir_read(&lfs, &dir, &info) => 1;
119            strcmp(info.name, path) => 0;
120            info.size => 0;
121
122            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
123            lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
124            lfs_file_write(&lfs, &file, "hi", 2) => 2;
125            lfs_file_close(&lfs, &file) => 0;
126        }
127        lfs_dir_read(&lfs, &dir, &info) => 0;
128
129        lfs_dir_rewind(&lfs, &dir) => 0;
130        lfs_dir_read(&lfs, &dir, &info) => 1;
131        lfs_dir_read(&lfs, &dir, &info) => 1;
132        for (unsigned i = 0; i < COUNT; i++) {
133            char path[1024];
134            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
135            lfs_dir_read(&lfs, &dir, &info) => 1;
136            strcmp(info.name, path) => 0;
137            info.size => 2;
138
139            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
140            lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
141            lfs_file_write(&lfs, &file, "hi", 2) => 2;
142            lfs_file_close(&lfs, &file) => 0;
143        }
144        lfs_dir_read(&lfs, &dir, &info) => 0;
145
146        lfs_dir_rewind(&lfs, &dir) => 0;
147        lfs_dir_read(&lfs, &dir, &info) => 1;
148        lfs_dir_read(&lfs, &dir, &info) => 1;
149        for (unsigned i = 0; i < COUNT; i++) {
150            char path[1024];
151            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
152            lfs_dir_read(&lfs, &dir, &info) => 1;
153            strcmp(info.name, path) => 0;
154            info.size => 2;
155        }
156        lfs_dir_read(&lfs, &dir, &info) => 0;
157        lfs_dir_close(&lfs, &dir) => 0;
158
159        for (unsigned i = 0; i < COUNT; i++) {
160            char path[1024];
161            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
162            lfs_remove(&lfs, path) => 0;
163        }
164    }
165    lfs_unmount(&lfs) => 0;
166'''
167
168# reentrant testing for relocations, this is the same as the
169# orphan testing, except here we also set block_cycles so that
170# almost every tree operation needs a relocation
171[cases.test_relocations_reentrant]
172reentrant = true
173# TODO fix this case, caused by non-DAG trees
174# NOTE the second condition is required
175if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
176defines = [
177    {FILES=6,  DEPTH=1, CYCLES=20, BLOCK_CYCLES=1},
178    {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1},
179    {FILES=3,  DEPTH=3, CYCLES=20, BLOCK_CYCLES=1},
180]
181code = '''
182    lfs_t lfs;
183    int err = lfs_mount(&lfs, cfg);
184    if (err) {
185        lfs_format(&lfs, cfg) => 0;
186        lfs_mount(&lfs, cfg) => 0;
187    }
188
189    uint32_t prng = 1;
190    const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
191    for (unsigned i = 0; i < CYCLES; i++) {
192        // create random path
193        char full_path[256];
194        for (unsigned d = 0; d < DEPTH; d++) {
195            sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
196        }
197
198        // if it does not exist, we create it, else we destroy
199        struct lfs_info info;
200        int res = lfs_stat(&lfs, full_path, &info);
201        if (res == LFS_ERR_NOENT) {
202            // create each directory in turn, ignore if dir already exists
203            for (unsigned d = 0; d < DEPTH; d++) {
204                char path[1024];
205                strcpy(path, full_path);
206                path[2*d+2] = '\0';
207                err = lfs_mkdir(&lfs, path);
208                assert(!err || err == LFS_ERR_EXIST);
209            }
210
211            for (unsigned d = 0; d < DEPTH; d++) {
212                char path[1024];
213                strcpy(path, full_path);
214                path[2*d+2] = '\0';
215                lfs_stat(&lfs, path, &info) => 0;
216                assert(strcmp(info.name, &path[2*d+1]) == 0);
217                assert(info.type == LFS_TYPE_DIR);
218            }
219        } else {
220            // is valid dir?
221            assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
222            assert(info.type == LFS_TYPE_DIR);
223
224            // try to delete path in reverse order, ignore if dir is not empty
225            for (unsigned d = DEPTH-1; d+1 > 0; d--) {
226                char path[1024];
227                strcpy(path, full_path);
228                path[2*d+2] = '\0';
229                err = lfs_remove(&lfs, path);
230                assert(!err || err == LFS_ERR_NOTEMPTY);
231            }
232
233            lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
234        }
235    }
236    lfs_unmount(&lfs) => 0;
237'''
238
239# reentrant testing for relocations, but now with random renames!
240[cases.test_relocations_reentrant_renames]
241reentrant = true
242# TODO fix this case, caused by non-DAG trees
243# NOTE the second condition is required
244if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT'
245defines = [
246    {FILES=6,  DEPTH=1, CYCLES=20, BLOCK_CYCLES=1},
247    {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1},
248    {FILES=3,  DEPTH=3, CYCLES=20, BLOCK_CYCLES=1},
249]
250code = '''
251    lfs_t lfs;
252    int err = lfs_mount(&lfs, cfg);
253    if (err) {
254        lfs_format(&lfs, cfg) => 0;
255        lfs_mount(&lfs, cfg) => 0;
256    }
257
258    uint32_t prng = 1;
259    const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
260    for (unsigned i = 0; i < CYCLES; i++) {
261        // create random path
262        char full_path[256];
263        for (unsigned d = 0; d < DEPTH; d++) {
264            sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
265        }
266
267        // if it does not exist, we create it, else we destroy
268        struct lfs_info info;
269        int res = lfs_stat(&lfs, full_path, &info);
270        assert(!res || res == LFS_ERR_NOENT);
271        if (res == LFS_ERR_NOENT) {
272            // create each directory in turn, ignore if dir already exists
273            for (unsigned d = 0; d < DEPTH; d++) {
274                char path[1024];
275                strcpy(path, full_path);
276                path[2*d+2] = '\0';
277                err = lfs_mkdir(&lfs, path);
278                assert(!err || err == LFS_ERR_EXIST);
279            }
280
281            for (unsigned d = 0; d < DEPTH; d++) {
282                char path[1024];
283                strcpy(path, full_path);
284                path[2*d+2] = '\0';
285                lfs_stat(&lfs, path, &info) => 0;
286                assert(strcmp(info.name, &path[2*d+1]) == 0);
287                assert(info.type == LFS_TYPE_DIR);
288            }
289        } else {
290            assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0);
291            assert(info.type == LFS_TYPE_DIR);
292
293            // create new random path
294            char new_path[256];
295            for (unsigned d = 0; d < DEPTH; d++) {
296                sprintf(&new_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]);
297            }
298
299            // if new path does not exist, rename, otherwise destroy
300            res = lfs_stat(&lfs, new_path, &info);
301            assert(!res || res == LFS_ERR_NOENT);
302            if (res == LFS_ERR_NOENT) {
303                // stop once some dir is renamed
304                for (unsigned d = 0; d < DEPTH; d++) {
305                    char path[1024];
306                    strcpy(&path[2*d], &full_path[2*d]);
307                    path[2*d+2] = '\0';
308                    strcpy(&path[128+2*d], &new_path[2*d]);
309                    path[128+2*d+2] = '\0';
310                    err = lfs_rename(&lfs, path, path+128);
311                    assert(!err || err == LFS_ERR_NOTEMPTY);
312                    if (!err) {
313                        strcpy(path, path+128);
314                    }
315                }
316
317                for (unsigned d = 0; d < DEPTH; d++) {
318                    char path[1024];
319                    strcpy(path, new_path);
320                    path[2*d+2] = '\0';
321                    lfs_stat(&lfs, path, &info) => 0;
322                    assert(strcmp(info.name, &path[2*d+1]) == 0);
323                    assert(info.type == LFS_TYPE_DIR);
324                }
325
326                lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
327            } else {
328                // try to delete path in reverse order,
329                // ignore if dir is not empty
330                for (unsigned d = DEPTH-1; d+1 > 0; d--) {
331                    char path[1024];
332                    strcpy(path, full_path);
333                    path[2*d+2] = '\0';
334                    err = lfs_remove(&lfs, path);
335                    assert(!err || err == LFS_ERR_NOTEMPTY);
336                }
337
338                lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT;
339            }
340        }
341    }
342    lfs_unmount(&lfs) => 0;
343'''
344