1 /*
2  * Copyright (c) 2019 Peter Bigot Consulting, LLC
3  * Copyright (c) 2023 Antmicro
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/ztest.h>
10 #include <zephyr/fs/fs.h>
11 
12 #include "test_fs_util.h"
13 
14 #define HELLO "hello"
15 #define GOODBYE "goodbye"
16 
17 /* Mount point for the tests should be provided by test runner.
18  * File system should be mounted before tests are started.
19  */
20 extern struct fs_mount_t *fs_basic_test_mp;
21 
create_write_hello(const struct fs_mount_t * mp)22 static int create_write_hello(const struct fs_mount_t *mp)
23 {
24 	struct testfs_path path;
25 	struct fs_file_t file;
26 
27 	fs_file_t_init(&file);
28 	TC_PRINT("creating and writing file\n");
29 
30 	zassert_equal(fs_open(&file,
31 			      testfs_path_init(&path, mp,
32 					       HELLO,
33 					       TESTFS_PATH_END),
34 			      FS_O_CREATE | FS_O_RDWR),
35 		      0,
36 		      "open hello failed");
37 
38 	struct fs_dirent stat;
39 
40 	zassert_equal(fs_stat(path.path, &stat),
41 		      0,
42 		      "stat new hello failed");
43 
44 	zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
45 		      "stat new hello not file");
46 	zassert_str_equal(stat.name, HELLO, "stat new hello not hello");
47 	zassert_equal(stat.size, 0,
48 		      "stat new hello not empty");
49 
50 	zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
51 		      TESTFS_BUFFER_SIZE,
52 		      "write constant failed");
53 
54 	zassert_equal(fs_stat(path.path, &stat),
55 		      0,
56 		      "stat written hello failed");
57 
58 	zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
59 		      "stat written hello not file");
60 	zassert_str_equal(stat.name, HELLO, "stat written hello not hello");
61 
62 	/* Anomalous behavior requiring upstream response */
63 	if (mp->type == FS_LITTLEFS) {
64 		/* VARIATION POINT: littlefs does not update the file size of
65 		 * an open file.  See upstream issue #250.
66 		 */
67 		zassert_equal(stat.size, 0,
68 			      "stat written hello bad size");
69 	}
70 
71 	zassert_equal(fs_close(&file), 0,
72 		      "close hello failed");
73 
74 	zassert_equal(fs_stat(path.path, &stat),
75 		      0,
76 		      "stat closed hello failed");
77 
78 	zassert_equal(stat.type, FS_DIR_ENTRY_FILE,
79 		      "stat closed hello not file");
80 	zassert_str_equal(stat.name, HELLO, "stat closed hello not hello");
81 	zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
82 		      "stat closed hello badsize");
83 
84 	return TC_PASS;
85 }
86 
verify_hello(const struct fs_mount_t * mp)87 static int verify_hello(const struct fs_mount_t *mp)
88 {
89 	struct testfs_path path;
90 	struct fs_file_t file;
91 
92 	fs_file_t_init(&file);
93 	TC_PRINT("opening and verifying file\n");
94 
95 	zassert_equal(fs_open(&file,
96 			      testfs_path_init(&path, mp,
97 					       HELLO,
98 					       TESTFS_PATH_END),
99 			      FS_O_CREATE | FS_O_RDWR),
100 		      0,
101 		      "verify hello open failed");
102 
103 	zassert_equal(fs_tell(&file), 0U,
104 		      "verify hello open tell failed");
105 
106 	zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
107 		      TESTFS_BUFFER_SIZE,
108 		      "verify hello at start failed");
109 
110 	zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
111 		      "verify hello read tell failed");
112 
113 	zassert_equal(fs_close(&file), 0,
114 		      "verify close hello failed");
115 
116 	return TC_PASS;
117 }
118 
seek_within_hello(const struct fs_mount_t * mp)119 static int seek_within_hello(const struct fs_mount_t *mp)
120 {
121 	struct testfs_path path;
122 	struct fs_file_t file;
123 
124 	fs_file_t_init(&file);
125 	TC_PRINT("seek and tell in file\n");
126 
127 	zassert_equal(fs_open(&file,
128 			      testfs_path_init(&path, mp,
129 					       HELLO,
130 					       TESTFS_PATH_END),
131 			      FS_O_CREATE | FS_O_RDWR),
132 		      0,
133 		      "verify hello open failed");
134 
135 	zassert_equal(fs_tell(&file), 0U,
136 		      "verify hello open tell failed");
137 
138 	struct fs_dirent stat;
139 
140 	zassert_equal(fs_stat(path.path, &stat),
141 		      0,
142 		      "stat old hello failed");
143 	zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
144 		      "stat old hello bad size");
145 
146 	off_t pos = stat.size / 4;
147 
148 	zassert_equal(fs_seek(&file, pos, FS_SEEK_SET),
149 		      0,
150 		      "verify hello seek near mid failed");
151 
152 	zassert_equal(fs_tell(&file), pos,
153 		      "verify hello tell near mid failed");
154 
155 	zassert_equal(testfs_verify_incrementing(&file, pos, TESTFS_BUFFER_SIZE),
156 		      TESTFS_BUFFER_SIZE - pos,
157 		      "verify hello at middle failed");
158 
159 	zassert_equal(fs_tell(&file), stat.size,
160 		      "verify hello read middle tell failed");
161 
162 	zassert_equal(fs_seek(&file, -stat.size, FS_SEEK_CUR),
163 		      0,
164 		      "verify hello seek back from cur failed");
165 
166 	zassert_equal(fs_tell(&file), 0U,
167 		      "verify hello tell back from cur failed");
168 
169 	zassert_equal(fs_seek(&file, -pos, FS_SEEK_END),
170 		      0,
171 		      "verify hello seek from end failed");
172 
173 	zassert_equal(fs_tell(&file), stat.size - pos,
174 		      "verify hello tell from end failed");
175 
176 	zassert_equal(testfs_verify_incrementing(&file, stat.size - pos,
177 						 TESTFS_BUFFER_SIZE),
178 		      pos,
179 		      "verify hello at post middle failed");
180 
181 	zassert_equal(fs_close(&file), 0,
182 		      "verify close hello failed");
183 
184 	return TC_PASS;
185 }
186 
truncate_hello(const struct fs_mount_t * mp)187 static int truncate_hello(const struct fs_mount_t *mp)
188 {
189 	struct testfs_path path;
190 	struct fs_file_t file;
191 
192 	fs_file_t_init(&file);
193 	TC_PRINT("truncate in file\n");
194 
195 	zassert_equal(fs_open(&file,
196 			      testfs_path_init(&path, mp,
197 					       HELLO,
198 					       TESTFS_PATH_END),
199 			      FS_O_CREATE | FS_O_RDWR),
200 		      0,
201 		      "verify hello open failed");
202 
203 	struct fs_dirent stat;
204 
205 	zassert_equal(fs_stat(path.path, &stat),
206 		      0,
207 		      "stat old hello failed");
208 	zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
209 		      "stat old hello bad size");
210 
211 	off_t pos = 3 * stat.size / 4;
212 
213 	zassert_equal(fs_tell(&file), 0U,
214 		      "truncate initial tell failed");
215 
216 	zassert_equal(fs_truncate(&file, pos),
217 		      0,
218 		      "truncate 3/4 failed");
219 
220 	zassert_equal(fs_tell(&file), 0U,
221 		      "truncate post tell failed");
222 
223 	zassert_equal(fs_stat(path.path, &stat),
224 		      0,
225 		      "stat open 3/4 failed");
226 
227 	/* Anomalous behavior requiring upstream response */
228 	if (mp->type == FS_LITTLEFS) {
229 		/* VARIATION POINT: littlefs does not update the file size of
230 		 * an open file.  See upstream issue #250.
231 		 */
232 		zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
233 			      "stat open 3/4 bad size");
234 	}
235 
236 	zassert_equal(testfs_verify_incrementing(&file, 0, 64),
237 		      48,
238 		      "post truncate content unexpected");
239 
240 	zassert_equal(fs_close(&file), 0,
241 		      "post truncate close failed");
242 
243 	/* After close size is correct. */
244 	zassert_equal(fs_stat(path.path, &stat),
245 		      0,
246 		      "stat closed truncated failed");
247 	zassert_equal(stat.size, pos,
248 		      "stat closed truncated bad size");
249 
250 	return TC_PASS;
251 }
252 
unlink_hello(const struct fs_mount_t * mp)253 static int unlink_hello(const struct fs_mount_t *mp)
254 {
255 	struct testfs_path path;
256 
257 	TC_PRINT("unlink hello\n");
258 
259 	testfs_path_init(&path, mp,
260 			 HELLO,
261 			 TESTFS_PATH_END);
262 
263 	struct fs_dirent stat;
264 
265 	zassert_equal(fs_stat(path.path, &stat),
266 		      0,
267 		      "stat existing hello failed");
268 	zassert_equal(fs_unlink(path.path),
269 		      0,
270 		      "unlink hello failed");
271 	zassert_equal(fs_stat(path.path, &stat),
272 		      -ENOENT,
273 		      "stat existing hello failed");
274 
275 	return TC_PASS;
276 }
277 
sync_goodbye(const struct fs_mount_t * mp)278 static int sync_goodbye(const struct fs_mount_t *mp)
279 {
280 	struct testfs_path path;
281 	struct fs_file_t file;
282 
283 	fs_file_t_init(&file);
284 	TC_PRINT("sync goodbye\n");
285 
286 	zassert_equal(fs_open(&file,
287 			      testfs_path_init(&path, mp,
288 					       GOODBYE,
289 					       TESTFS_PATH_END),
290 			      FS_O_CREATE | FS_O_RDWR),
291 		      0,
292 		      "sync goodbye failed");
293 
294 	struct fs_dirent stat;
295 
296 	zassert_equal(fs_stat(path.path, &stat),
297 		      0,
298 		      "stat existing hello failed");
299 	zassert_equal(stat.size, 0,
300 		      "stat new goodbye not empty");
301 
302 	zassert_equal(testfs_write_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
303 		      TESTFS_BUFFER_SIZE,
304 		      "write goodbye failed");
305 
306 	zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
307 		      "tell goodbye failed");
308 
309 	if (true && mp->type == FS_LITTLEFS) {
310 		/* Upstream issue #250 */
311 		zassert_equal(stat.size, 0,
312 			      "stat new goodbye not empty");
313 	}
314 
315 	zassert_equal(fs_sync(&file), 0,
316 		      "sync goodbye failed");
317 
318 	zassert_equal(fs_tell(&file), TESTFS_BUFFER_SIZE,
319 		      "tell synced moved");
320 
321 	zassert_equal(fs_stat(path.path, &stat),
322 		      0,
323 		      "stat existing hello failed");
324 	printk("sync size %u\n", (uint32_t)stat.size);
325 
326 	zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
327 		      "stat synced goodbye not correct");
328 
329 	zassert_equal(fs_close(&file), 0,
330 		      "post sync close failed");
331 
332 	/* After close size is correct. */
333 	zassert_equal(fs_stat(path.path, &stat),
334 		      0,
335 		      "stat sync failed");
336 	zassert_equal(stat.size, TESTFS_BUFFER_SIZE,
337 		      "stat sync bad size");
338 
339 	return TC_PASS;
340 }
341 
verify_goodbye(const struct fs_mount_t * mp)342 static int verify_goodbye(const struct fs_mount_t *mp)
343 {
344 	struct testfs_path path;
345 	struct fs_file_t file;
346 
347 	fs_file_t_init(&file);
348 	TC_PRINT("verify goodbye\n");
349 
350 	zassert_equal(fs_open(&file,
351 			      testfs_path_init(&path, mp,
352 					       GOODBYE,
353 					       TESTFS_PATH_END),
354 			      FS_O_CREATE | FS_O_RDWR),
355 		      0,
356 		      "verify goodbye failed");
357 
358 	zassert_equal(testfs_verify_incrementing(&file, 0, TESTFS_BUFFER_SIZE),
359 		      TESTFS_BUFFER_SIZE,
360 		      "write goodbye failed");
361 
362 	zassert_equal(fs_close(&file), 0,
363 		      "post sync close failed");
364 
365 	return TC_PASS;
366 }
367 
test_fs_basic(void)368 void test_fs_basic(void)
369 {
370 
371 	zassert_equal(fs_mount(fs_basic_test_mp), 0,
372 		      "mount failed");
373 
374 	zassert_equal(create_write_hello(fs_basic_test_mp), TC_PASS,
375 		      "write hello failed");
376 
377 	zassert_equal(verify_hello(fs_basic_test_mp), TC_PASS,
378 		      "verify hello failed");
379 
380 	zassert_equal(seek_within_hello(fs_basic_test_mp), TC_PASS,
381 		      "seek within hello failed");
382 
383 	zassert_equal(truncate_hello(fs_basic_test_mp), TC_PASS,
384 		      "truncate hello failed");
385 
386 	zassert_equal(unlink_hello(fs_basic_test_mp), TC_PASS,
387 		      "unlink hello failed");
388 
389 	zassert_equal(sync_goodbye(fs_basic_test_mp), TC_PASS,
390 		      "sync goodbye failed");
391 
392 	TC_PRINT("unmounting %s\n", fs_basic_test_mp->mnt_point);
393 	zassert_equal(fs_unmount(fs_basic_test_mp), 0,
394 		      "unmount small failed");
395 
396 	k_sleep(K_MSEC(100));   /* flush log messages */
397 	TC_PRINT("checking double unmount diagnoses\n");
398 
399 	zassert_equal(fs_unmount(fs_basic_test_mp), -EINVAL,
400 		      "unmount unmounted failed");
401 
402 	zassert_equal(fs_mount(fs_basic_test_mp), 0,
403 		      "mount failed");
404 
405 	zassert_equal(verify_goodbye(fs_basic_test_mp), TC_PASS,
406 		      "verify goodbye failed");
407 
408 	zassert_equal(fs_unmount(fs_basic_test_mp), 0,
409 		      "unmount2 small failed");
410 }
411