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