1 /*
2  * Copyright (c) 2020 Intel Corporation.
3  * Copyright (c) 2020 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @brief Test cases for filesystem api
10  *
11  * @defgroup filesystem_api File system API
12  * @ingroup filesystem
13  * @{
14  * @}
15  */
16 
17 #include "test_fs.h"
18 #include <stdio.h>
19 #include <string.h>
20 
21 struct fs_file_system_t null_fs = {NULL};
22 
23 static struct test_fs_data test_data;
24 static struct fs_mount_t test_fs_mnt_1 = {
25 		.type = TEST_FS_1,
26 		.mnt_point = TEST_FS_MNTP,
27 		.fs_data = &test_data,
28 };
29 
30 static struct fs_mount_t test_fs_mnt_already_mounted_same_data = {
31 		.type = TEST_FS_2,
32 		.mnt_point = "/OTHER",
33 		.fs_data = &test_data,
34 };
35 
36 static struct test_fs_data test_data1;
37 static struct fs_mount_t test_fs_mnt_unsupported_fs = {
38 		.type = FS_TYPE_EXTERNAL_BASE,
39 		.mnt_point = "/MMCBLOCK:",
40 		.fs_data = &test_data1,
41 };
42 
43 /* invalid name of mount point, not start with '/' */
44 static struct test_fs_data test_data2;
45 static struct fs_mount_t test_fs_mnt_invalid_root_1 = {
46 		.type = TEST_FS_2,
47 		.mnt_point = "SDA:",
48 		.fs_data = &test_data2,
49 };
50 
51 /* length of the name of mount point is too short */
52 static struct test_fs_data test_data3;
53 static struct fs_mount_t test_fs_mnt_invalid_root_2 = {
54 		.type = TEST_FS_2,
55 		.mnt_point = "/",
56 		.fs_data = &test_data3,
57 };
58 
59 /* NULL mount point */
60 static struct test_fs_data test_data4;
61 static struct fs_mount_t test_fs_mnt_invalid_root_3 = {
62 		.type = TEST_FS_2,
63 		.mnt_point = NULL,
64 		.fs_data = &test_data4,
65 };
66 
67 static struct test_fs_data test_data5;
68 static struct fs_mount_t test_fs_mnt_already_mounted = {
69 		.type = TEST_FS_2,
70 		.mnt_point = TEST_FS_MNTP,
71 		.fs_data = &test_data5,
72 };
73 
74 /* for test_fs, name of mount point must end with ':' */
75 static struct test_fs_data test_data6;
76 static struct fs_mount_t test_fs_mnt_invalid_mntp = {
77 		.type = TEST_FS_2,
78 		.mnt_point = "/SDA",
79 		.fs_data = &test_data6,
80 };
81 
82 #define NOOP_MNTP "/SDCD:"
83 static struct test_fs_data test_data7;
84 static struct fs_mount_t test_fs_mnt_no_op = {
85 		.type = TEST_FS_2,
86 		.mnt_point = NOOP_MNTP,
87 		.fs_data = &test_data7,
88 };
89 
90 static struct fs_file_t filep;
91 static struct fs_file_t err_filep;
92 static const char test_str[] = "hello world!";
93 
94 /**
95  * @brief Test fs_file_t_init initializer
96  */
ZTEST(fs_api_dir_file,test_fs_file_t_init)97 ZTEST(fs_api_dir_file, test_fs_file_t_init)
98 {
99 	struct fs_file_t fst;
100 
101 	memset(&fst, 0xff, sizeof(fst));
102 
103 	fs_file_t_init(&fst);
104 	zassert_equal(fst.mp, NULL, "Expected to be initialized to NULL");
105 	zassert_equal(fst.filep, NULL, "Expected to be initialized to NULL");
106 	zassert_equal(fst.flags, 0, "Expected to be initialized to 0");
107 }
108 
109 /**
110  * @brief Test fs_dir_t_init initializer
111  */
ZTEST(fs_api_dir_file,test_fs_dir_t_init)112 ZTEST(fs_api_dir_file, test_fs_dir_t_init)
113 {
114 	struct fs_dir_t dirp;
115 
116 	memset(&dirp, 0xff, sizeof(dirp));
117 
118 	fs_dir_t_init(&dirp);
119 	zassert_equal(dirp.mp, NULL, "Expected to be initialized to NULL");
120 	zassert_equal(dirp.dirp, NULL, "Expected to be initialized to NULL");
121 }
122 
123 /**
124  * @brief Test mount interface of filesystem
125  *
126  * @details
127  * Test fs_mount() interface in file system core
128  * Following test cases depend on file systems mounted in this test case
129  *
130  * @ingroup filesystem_api
131  */
test_mount(void)132 void test_mount(void)
133 {
134 	int ret;
135 
136 	TC_PRINT("\nmount tests:\n");
137 	TC_PRINT("Pass NULL pointer to fs_mount()\n");
138 	ret = fs_mount(NULL);
139 	zassert_not_equal(ret, 0, "Mount a NULL fs");
140 
141 	TC_PRINT("Mount an unsupported file system\n");
142 	ret = fs_mount(&test_fs_mnt_unsupported_fs);
143 	zassert_not_equal(ret, 0, "Mount an unsupported fs");
144 
145 	fs_register(TEST_FS_2, &temp_fs);
146 
147 	TC_PRINT("Mount to an invalid directory\n");
148 	ret = fs_mount(&test_fs_mnt_invalid_root_1);
149 	zassert_not_equal(ret, 0, "Mount to an invalid dir");
150 	ret = fs_mount(&test_fs_mnt_invalid_root_2);
151 	zassert_not_equal(ret, 0, "Mount dir name too short");
152 	ret = fs_mount(&test_fs_mnt_invalid_root_3);
153 	zassert_not_equal(ret, 0, "Mount point is NULL");
154 	ret = fs_mount(&test_fs_mnt_invalid_mntp);
155 	zassert_not_equal(ret, 0, "Mount with invalid mount point");
156 
157 	ret = fs_mount(&test_fs_mnt_1);
158 	zassert_equal(ret, 0, "Error mounting fs");
159 
160 	TC_PRINT("Mount to a directory that has file system mounted already\n");
161 	ret = fs_mount(&test_fs_mnt_already_mounted);
162 	zassert_not_equal(ret, 0, "Mount to a mounted dir");
163 
164 	TC_PRINT("Mount using same private data as already mounted system\n");
165 	ret = fs_mount(&test_fs_mnt_already_mounted_same_data);
166 	zassert_equal(ret, -EBUSY, "Re-mount using same data should have failed");
167 
168 	fs_unregister(TEST_FS_2, &temp_fs);
169 	memset(&null_fs, 0, sizeof(null_fs));
170 	fs_register(TEST_FS_2, &null_fs);
171 
172 	TC_PRINT("Mount a file system has no interface implemented\n");
173 	ret = fs_mount(&test_fs_mnt_no_op);
174 	zassert_not_equal(ret, 0, "Mount to a fs without op interface");
175 
176 	/* mount an file system has no unmount functionality */
177 	null_fs.mount = temp_fs.mount;
178 	ret = fs_mount(&test_fs_mnt_no_op);
179 	zassert_equal(ret, 0, "fs has no unmount functionality can be mounted");
180 }
181 
182 /**
183  * @brief Test fs_unmount() interface in file system core
184  *
185  * @ingroup filesystem_api
186  */
test_unmount(void)187 void test_unmount(void)
188 {
189 	int ret;
190 
191 	TC_PRINT("\nunmount tests:\n");
192 
193 	TC_PRINT("\nunmount nothing:\n");
194 	ret = fs_unmount(NULL);
195 	zassert_not_equal(ret, 0, "Unmount a NULL fs");
196 
197 	TC_PRINT("\nunmount file system that has never been mounted:\n");
198 	ret = fs_unmount(&test_fs_mnt_unsupported_fs);
199 	zassert_not_equal(ret, 0, "Unmount a never mounted fs");
200 
201 	ret = fs_unmount(&test_fs_mnt_1);
202 	zassert_true(ret >= 0, "Fail to unmount fs");
203 
204 	TC_PRINT("\nunmount file system multiple times:\n");
205 	test_fs_mnt_1.fs = &temp_fs;
206 	ret = fs_unmount(&test_fs_mnt_1);
207 	zassert_not_equal(ret, 0, "Unmount an unmounted fs");
208 
209 	TC_PRINT("unmount a file system has no unmount functionality\n");
210 	ret = fs_unmount(&test_fs_mnt_no_op);
211 	zassert_not_equal(ret, 0, "Unmount a fs has no unmount functionality");
212 	/* assign a unmount interface to null_fs to unmount it */
213 	null_fs.unmount = temp_fs.unmount;
214 	ret = fs_unmount(&test_fs_mnt_no_op);
215 	zassert_equal(ret, 0, "file system should be unmounted");
216 	/* TEST_FS_2 is registered in test_mount(), unregister it here */
217 	fs_unregister(TEST_FS_2, &null_fs);
218 }
219 
220 /**
221  * @brief Test fs_statvfs() interface in file system core
222  *
223  * @ingroup filesystem_api
224  */
ZTEST(fs_api_dir_file,test_file_statvfs)225 ZTEST(fs_api_dir_file, test_file_statvfs)
226 {
227 	struct fs_statvfs stat;
228 	int ret;
229 
230 	ret = fs_statvfs(NULL, &stat);
231 	zassert_not_equal(ret, 0, "Pass NULL for path pointer");
232 	ret = fs_statvfs(TEST_FS_MNTP, NULL);
233 	zassert_not_equal(ret, 0, "Pass NULL for stat structure pointer");
234 
235 	ret = fs_statvfs("/", &stat);
236 	zassert_not_equal(ret, 0, "Path name too short");
237 
238 	ret = fs_statvfs("SDCARD:", &stat);
239 	zassert_not_equal(ret, 0, "Path name should start with /");
240 
241 	ret = fs_statvfs("/SDCARD:", &stat);
242 	zassert_not_equal(ret, 0, "Get volume info by no-exist path");
243 
244 	/* System with no statvfs interface */
245 	ret = fs_statvfs(NOOP_MNTP, &stat);
246 	zassert_equal(ret, -ENOTSUP, "fs has no statvfs functionality");
247 
248 	ret = fs_statvfs(TEST_FS_MNTP, &stat);
249 	zassert_equal(ret, 0, "Error getting volume stats");
250 	TC_PRINT("\n");
251 	TC_PRINT("Optimal transfer block size   = %lu\n", stat.f_bsize);
252 	TC_PRINT("Allocation unit size          = %lu\n", stat.f_frsize);
253 	TC_PRINT("Volume size in f_frsize units = %lu\n", stat.f_blocks);
254 	TC_PRINT("Free space in f_frsize units  = %lu\n", stat.f_bfree);
255 }
256 
257 /**
258  * @brief Test fs_mkdir() interface in file system core
259  *
260  * @ingroup filesystem_api
261  */
test_mkdir(void)262 void test_mkdir(void)
263 {
264 	int ret;
265 
266 	TC_PRINT("\nmkdir tests:\n");
267 
268 	ret = fs_mkdir(NULL);
269 	zassert_not_equal(ret, 0, "Create a NULL directory");
270 
271 	ret = fs_mkdir("d");
272 	zassert_not_equal(ret, 0, "Create dir with too short name");
273 
274 	ret = fs_mkdir("SDCARD:/testdir");
275 	zassert_not_equal(ret, 0, "Create dir with wrong path");
276 
277 	ret = fs_mkdir("/SDCARD:/testdir");
278 	zassert_not_equal(ret, 0, "Create dir in no fs mounted dir");
279 
280 	ret = fs_mkdir(TEST_FS_MNTP);
281 	zassert_not_equal(ret, 0, "Should not create root dir");
282 
283 	ret = fs_mkdir(NOOP_MNTP"/testdir");
284 	zassert_not_equal(ret, 0, "Filesystem has no mkdir interface");
285 
286 	ret = fs_mkdir(TEST_DIR);
287 	zassert_equal(ret, 0, "Error creating dir");
288 }
289 
290 /**
291  * @brief Test fs_opendir() interface in file system core
292  *
293  * @ingroup filesystem_api
294  */
test_opendir(void)295 void test_opendir(void)
296 {
297 	int ret;
298 	struct fs_dir_t dirp, dirp2, dirp3;
299 
300 	TC_PRINT("\nopendir tests:\n");
301 
302 	fs_dir_t_init(&dirp);
303 	fs_dir_t_init(&dirp2);
304 	fs_dir_t_init(&dirp3);
305 
306 	TC_PRINT("Test null path\n");
307 	ret = fs_opendir(NULL, NULL);
308 	zassert_not_equal(ret, 0, "Open dir with NULL pointer parameter");
309 
310 	TC_PRINT("Test directory without root path\n");
311 	ret = fs_opendir(&dirp, "ab");
312 	zassert_not_equal(ret, 0, "Can't open dir without root path");
313 
314 	TC_PRINT("Test directory without name\n");
315 	ret = fs_opendir(&dirp, "");
316 	zassert_not_equal(ret, 0, "Can't open dir without path name");
317 
318 	TC_PRINT("Test not existing mount point\n");
319 	ret = fs_opendir(&dirp, "/SDCARD:/test_dir");
320 	zassert_not_equal(ret, 0, "Open dir in an unmounted fs");
321 
322 	TC_PRINT("Test filesystem has no opendir functionality\n");
323 	ret = fs_opendir(&dirp, NOOP_MNTP"/test_dir");
324 	zassert_not_equal(ret, 0, "Filesystem has no opendir functionality");
325 
326 	TC_PRINT("Test root directory\n");
327 	ret = fs_opendir(&dirp, "/");
328 	zassert_equal(ret, 0, "Fail to open root dir");
329 
330 	TC_PRINT("Double-open using occupied fs_dir_t object\n");
331 	ret = fs_opendir(&dirp, "/not_a_dir");
332 	zassert_equal(ret, -EBUSY, "Expected -EBUSY, got %d", ret);
333 
334 	ret = fs_opendir(&dirp2, TEST_DIR);
335 	zassert_equal(ret, 0, "Fail to open dir");
336 
337 	TC_PRINT("Double-open using occupied fs_dir_t object\n");
338 	ret = fs_opendir(&dirp2, "/xD");
339 	zassert_equal(ret, -EBUSY, "Expected -EBUSY, got %d", ret);
340 
341 	mock_opendir_result(-EIO);
342 	TC_PRINT("Transfer underlying FS error\n");
343 	ret = fs_opendir(&dirp3, TEST_DIR);
344 	mock_opendir_result(0);
345 	zassert_equal(ret, -EIO, "FS error not transferred\n");
346 }
347 
348 /**
349  * @brief Test fs_close() interface in file system core
350  *
351  * @ingroup filesystem_api
352  */
test_closedir(void)353 void test_closedir(void)
354 {
355 	int ret;
356 	struct fs_dir_t dirp;
357 
358 	TC_PRINT("\nclosedir tests: %s\n", TEST_DIR);
359 	fs_dir_t_init(&dirp);
360 	ret = fs_opendir(&dirp, TEST_DIR);
361 	zassert_equal(ret, 0, "Fail to open dir");
362 
363 	ret = fs_closedir(&dirp);
364 	zassert_equal(ret, 0, "Fail to close dir");
365 
366 	dirp.mp = &test_fs_mnt_1;
367 	ret = fs_closedir(&dirp);
368 	zassert_not_equal(ret, 0, "Should no close a closed dir");
369 
370 	dirp.mp = &test_fs_mnt_no_op;
371 	ret = fs_closedir(&dirp);
372 	zassert_not_equal(ret, 0, "Filesystem has no closedir interface");
373 }
374 
375 /**
376  * @brief Test Reuse fs_dir_t object from closed directory"
377  *
378  * @ingroup filesystem_api
379  */
test_opendir_closedir(void)380 void test_opendir_closedir(void)
381 {
382 	int ret;
383 	struct fs_dir_t dirp;
384 
385 	TC_PRINT("\nreuse fs_dir_t tests:\n");
386 
387 	fs_dir_t_init(&dirp);
388 
389 	TC_PRINT("Test: open root dir, close, open volume dir\n");
390 	ret = fs_opendir(&dirp, "/");
391 	zassert_equal(ret, 0, "Fail to open root dir");
392 
393 	ret = fs_closedir(&dirp);
394 	zassert_equal(ret, 0, "Fail to close dir");
395 
396 	ret = fs_opendir(&dirp, TEST_DIR);
397 	zassert_equal(ret, 0, "Fail to open dir");
398 
399 	TC_PRINT("Test: open volume dir, close, open root dir\n");
400 	ret = fs_closedir(&dirp);
401 	zassert_equal(ret, 0, "Fail to close dir");
402 
403 	ret = fs_opendir(&dirp, "/");
404 	zassert_equal(ret, 0, "Fail to open root dir");
405 }
406 
_test_lsdir(const char * path)407 static int _test_lsdir(const char *path)
408 {
409 	int ret;
410 	struct fs_dir_t dirp;
411 	struct fs_dirent entry;
412 
413 	TC_PRINT("\nlsdir tests:\n");
414 
415 	fs_dir_t_init(&dirp);
416 	memset(&entry, 0, sizeof(entry));
417 
418 	TC_PRINT("read an unopened dir\n");
419 	dirp.dirp = "somepath";
420 	ret = fs_readdir(&dirp, &entry);
421 	if (!ret) {
422 		return TC_FAIL;
423 	}
424 
425 	dirp.mp = &test_fs_mnt_no_op;
426 	ret = fs_readdir(&dirp, NULL);
427 	if (!ret) {
428 		return TC_FAIL;
429 	}
430 
431 	dirp.mp = &test_fs_mnt_1;
432 	ret = fs_readdir(&dirp, NULL);
433 	if (!ret) {
434 		return TC_FAIL;
435 	}
436 
437 	TC_PRINT("read an opened dir\n");
438 	fs_dir_t_init(&dirp);
439 	ret = fs_opendir(&dirp, path);
440 	if (ret) {
441 		if (path) {
442 			TC_PRINT("Error opening dir %s [%d]\n", path, ret);
443 		}
444 		return TC_FAIL;
445 	}
446 
447 	TC_PRINT("\nListing dir %s:\n", path);
448 	for (;;) {
449 		ret = fs_readdir(&dirp, &entry);
450 
451 		/* entry.name[0] == 0 means end-of-dir */
452 		if (ret || entry.name[0] == 0) {
453 			break;
454 		}
455 
456 		if (entry.type == FS_DIR_ENTRY_DIR) {
457 			TC_PRINT("[DIR ] %s\n", entry.name);
458 		} else {
459 			TC_PRINT("[FILE] %s (size = %zu)\n",
460 				entry.name, entry.size);
461 		}
462 	}
463 
464 	ret = fs_closedir(&dirp);
465 	if (ret) {
466 		TC_PRINT("Error close a directory\n");
467 		return TC_FAIL;
468 	}
469 
470 	return ret;
471 }
472 
473 /**
474  * @brief Test fs_readdir() interface in file system core
475  *
476  * @ingroup filesystem_api
477  */
test_lsdir(void)478 void test_lsdir(void)
479 {
480 	zassert_true(_test_lsdir(NULL) == TC_FAIL);
481 	zassert_true(_test_lsdir("/") == TC_PASS);
482 	zassert_true(_test_lsdir("/test") == TC_FAIL);
483 	zassert_true(_test_lsdir(TEST_DIR) == TC_PASS);
484 }
485 
486 /**
487  * @brief Test fs_open() interface in file system core
488  *
489  * @ingroup filesystem_api
490  */
test_file_open(void)491 void test_file_open(void)
492 {
493 	int ret;
494 
495 	TC_PRINT("\nOpen tests:\n");
496 	fs_file_t_init(&filep);
497 
498 	TC_PRINT("\nOpen a file without a path\n");
499 	ret = fs_open(&filep, NULL, FS_O_READ);
500 	zassert_not_equal(ret, 0, "Open a NULL file");
501 
502 	TC_PRINT("\nOpen a file with wrong abs path\n");
503 	ret = fs_open(&filep, "/", FS_O_READ);
504 	zassert_not_equal(ret, 0, "Open a file with wrong path");
505 
506 	TC_PRINT("\nOpen a file with wrong path\n");
507 	ret = fs_open(&filep, "test_file.txt", FS_O_READ);
508 	zassert_not_equal(ret, 0, "Open a file with wrong path");
509 
510 	TC_PRINT("\nOpen a file with wrong abs path\n");
511 	ret = fs_open(&filep, "/test_file.txt", FS_O_READ);
512 	zassert_not_equal(ret, 0, "Open a file with wrong abs path");
513 
514 	TC_PRINT("\nFilesystem has no open functionality\n");
515 	ret = fs_open(&filep, NOOP_MNTP"/test_file.txt", FS_O_READ);
516 	zassert_not_equal(ret, 0, "Filesystem has no open functionality");
517 
518 	ret = fs_open(&filep, TEST_FILE, FS_O_READ);
519 	zassert_equal(ret, 0, "Fail to open file");
520 
521 	TC_PRINT("\nDouble-open\n");
522 	ret = fs_open(&filep, TEST_FILE, FS_O_READ);
523 	zassert_equal(ret, -EBUSY, "Expected -EBUSY, got %d", ret);
524 
525 	TC_PRINT("\nReopen the same file");
526 	ret = fs_open(&filep, TEST_FILE, FS_O_READ);
527 	zassert_not_equal(ret, 0, "Reopen an opened file");
528 
529 	TC_PRINT("Opened file %s\n", TEST_FILE);
530 }
531 
_test_file_write(void)532 static int _test_file_write(void)
533 {
534 	ssize_t brw;
535 	int ret;
536 
537 	TC_PRINT("\nWrite tests:\n");
538 
539 	TC_PRINT("Write to an unopened file\n");
540 	fs_file_t_init(&err_filep);
541 	brw = fs_write(&err_filep, (char *)test_str, strlen(test_str));
542 	if (brw >= 0) {
543 		return TC_FAIL;
544 	}
545 
546 	TC_PRINT("Write to filesystem has no write interface\n");
547 	err_filep.mp = &test_fs_mnt_no_op;
548 	brw = fs_write(&err_filep, (char *)test_str, strlen(test_str));
549 	if (brw >= 0) {
550 		return TC_FAIL;
551 	}
552 
553 	ret = fs_seek(&filep, 0, FS_SEEK_SET);
554 	if (ret) {
555 		TC_PRINT("fs_seek failed [%d]\n", ret);
556 		fs_close(&filep);
557 		return ret;
558 	}
559 
560 	TC_PRINT("Write to file from a invalid source\n");
561 	brw = fs_write(&filep, NULL, strlen(test_str));
562 	if (brw >= 0) {
563 		return TC_FAIL;
564 	}
565 
566 	TC_PRINT("Data written:\"%s\"\n\n", test_str);
567 
568 	brw = fs_write(&filep, (char *)test_str, strlen(test_str));
569 	if (brw < 0) {
570 		TC_PRINT("Failed writing to file [%zd]\n", brw);
571 		fs_close(&filep);
572 		return brw;
573 	}
574 
575 	if (brw < strlen(test_str)) {
576 		TC_PRINT("Unable to complete write. Volume full.\n");
577 		TC_PRINT("Number of bytes written: [%zd]\n", brw);
578 		fs_close(&filep);
579 		return TC_FAIL;
580 	}
581 
582 	TC_PRINT("Data successfully written!\n");
583 
584 	return ret;
585 }
586 
587 /**
588  * @brief Test fs_write() interface in file system core
589  *
590  * @ingroup filesystem_api
591  */
test_file_write(void)592 void test_file_write(void)
593 {
594 	zassert_true(_test_file_write() == TC_PASS);
595 }
596 
_test_file_sync(void)597 static int _test_file_sync(void)
598 {
599 	int ret;
600 	ssize_t brw;
601 
602 	TC_PRINT("\nSync tests:\n");
603 
604 	TC_PRINT("sync an unopened file\n");
605 	fs_file_t_init(&err_filep);
606 	ret = fs_sync(&err_filep);
607 	if (!ret) {
608 		return TC_FAIL;
609 	}
610 
611 	TC_PRINT("sync to filesystem has no sync functionality\n");
612 	err_filep.mp = &test_fs_mnt_no_op;
613 	ret = fs_sync(&err_filep);
614 	if (!ret) {
615 		return TC_FAIL;
616 	}
617 
618 	fs_file_t_init(&filep);
619 	ret = fs_open(&filep, TEST_FILE, FS_O_RDWR);
620 
621 	for (;;) {
622 		brw = fs_write(&filep, (char *)test_str, strlen(test_str));
623 		if (brw < strlen(test_str)) {
624 			break;
625 		}
626 		ret = fs_sync(&filep);
627 		if (ret) {
628 			TC_PRINT("Error syncing file [%d]\n", ret);
629 			fs_close(&filep);
630 			return ret;
631 		}
632 
633 		ret = fs_tell(&filep);
634 		if (ret < 0) {
635 			TC_PRINT("Error tell file [%d]\n", ret);
636 			fs_close(&filep);
637 			return ret;
638 		}
639 
640 	}
641 
642 	TC_PRINT("Sync a overflowed file\n");
643 	ret = fs_sync(&filep);
644 	if (!ret) {
645 		fs_close(&filep);
646 		return TC_FAIL;
647 	}
648 
649 	TC_PRINT("Tell a overflowed file\n");
650 	ret = fs_tell(&filep);
651 	if (!ret) {
652 		fs_close(&filep);
653 		return TC_FAIL;
654 	}
655 
656 	fs_close(&filep);
657 	return TC_PASS;
658 }
659 
660 /**
661  * @brief Test fs_sync() interface in file system core
662  *
663  * @ingroup filesystem_api
664  */
ZTEST(fs_api_dir_file,test_file_sync)665 ZTEST(fs_api_dir_file, test_file_sync)
666 {
667 	zassert_true(_test_file_sync() == TC_PASS);
668 }
669 
670 /**
671  * @brief Test fs_read() interface in file system core
672  *
673  * @ingroup filesystem_api
674  */
test_file_read(void)675 void test_file_read(void)
676 {
677 	ssize_t brw;
678 	char read_buff[80];
679 	size_t sz = strlen(test_str);
680 
681 	TC_PRINT("\nRead tests:\n");
682 
683 	TC_PRINT("Read an unopened file\n");
684 	fs_file_t_init(&err_filep);
685 	brw = fs_read(&err_filep, read_buff, sz);
686 	zassert_false(brw >= 0, "Can't read an unopened file");
687 
688 	TC_PRINT("Filesystem has no read interface\n");
689 	err_filep.mp = &test_fs_mnt_no_op;
690 	brw = fs_read(&err_filep, read_buff, sz);
691 	zassert_false(brw >= 0, "Filesystem has no read interface");
692 
693 	TC_PRINT("Read to a invalid buffer\n");
694 	brw = fs_read(&filep, NULL, sz);
695 	zassert_false(brw >= 0, "Read data to a invalid buffer");
696 
697 	brw = fs_read(&filep, read_buff, sz);
698 	zassert_true(brw >= 0, "Fail to read file");
699 
700 	read_buff[brw] = 0;
701 	TC_PRINT("Data read:\"%s\"\n\n", read_buff);
702 
703 	zassert_str_equal(test_str, read_buff,
704 			  "Error - Data read does not match data written");
705 
706 	TC_PRINT("Data read matches data written\n");
707 }
708 
709 /**
710  * @brief fs_seek tests for expected ENOTSUP
711  *
712  * @ingroup filesystem_api
713  */
test_file_seek(void)714 void test_file_seek(void)
715 {
716 	struct fs_file_system_t backup = temp_fs;
717 
718 	/* Simulate tell and lseek not implemented */
719 	temp_fs.lseek = NULL;
720 	temp_fs.tell = NULL;
721 
722 	zassert_equal(fs_seek(&filep, 0, FS_SEEK_CUR),
723 		      -ENOTSUP,
724 		      "fs_seek not expected to be implemented");
725 	zassert_equal(fs_tell(&filep),
726 		      -ENOTSUP,
727 		      "fs_tell not expected to be implemented");
728 
729 	/* Restore fs API interface */
730 	temp_fs = backup;
731 }
732 
_test_file_truncate(void)733 static int _test_file_truncate(void)
734 {
735 	int ret;
736 	off_t orig_pos;
737 	char read_buff[80];
738 	ssize_t brw;
739 
740 	TC_PRINT("\nTruncate tests: max file size is 128byte\n");
741 
742 	TC_PRINT("\nTruncate, seek, tell an unopened file\n");
743 	fs_file_t_init(&err_filep);
744 	ret = fs_truncate(&err_filep, 256);
745 	if (!ret) {
746 		return TC_FAIL;
747 	}
748 	ret = fs_seek(&err_filep, 0, FS_SEEK_END);
749 	if (!ret) {
750 		return TC_FAIL;
751 	}
752 
753 	ret = fs_tell(&err_filep);
754 	if (!ret) {
755 		return TC_FAIL;
756 	}
757 
758 	TC_PRINT("\nTruncate, seek, tell fs has no these functionality\n");
759 	err_filep.mp = &test_fs_mnt_no_op;
760 	ret = fs_truncate(&err_filep, 256);
761 	if (!ret) {
762 		return TC_FAIL;
763 	}
764 	ret = fs_seek(&err_filep, 0, FS_SEEK_END);
765 	if (!ret) {
766 		return TC_FAIL;
767 	}
768 
769 	ret = fs_tell(&err_filep);
770 	if (!ret) {
771 		return TC_FAIL;
772 	}
773 
774 	TC_PRINT("Truncating to size larger than 128byte\n");
775 	ret = fs_truncate(&filep, 256);
776 	if (!ret) {
777 		fs_close(&filep);
778 		return TC_FAIL;
779 	}
780 
781 	/* Test truncating to 0 size */
782 	TC_PRINT("\nTesting shrink to 0 size\n");
783 	ret = fs_truncate(&filep, 0);
784 	if (ret) {
785 		TC_PRINT("fs_truncate failed [%d]\n", ret);
786 		fs_close(&filep);
787 		return ret;
788 	}
789 
790 	TC_PRINT("File seek from invalid whence\n");
791 	ret = fs_seek(&filep, 0, 100);
792 	if (!ret) {
793 		fs_close(&filep);
794 		return TC_FAIL;
795 	}
796 
797 	fs_seek(&filep, 0, FS_SEEK_END);
798 	if (fs_tell(&filep) > 0) {
799 		TC_PRINT("Failed truncating to size 0\n");
800 		fs_close(&filep);
801 		return TC_FAIL;
802 	}
803 
804 	TC_PRINT("Testing write after truncating\n");
805 	ret = _test_file_write();
806 	if (ret) {
807 		TC_PRINT("Write failed after truncating\n");
808 		return ret;
809 	}
810 
811 	fs_seek(&filep, 0, FS_SEEK_END);
812 
813 	orig_pos = fs_tell(&filep);
814 	TC_PRINT("Original size of file = %d\n", (int)orig_pos);
815 
816 	/* Test shrinking file */
817 	TC_PRINT("\nTesting shrinking\n");
818 	ret = fs_truncate(&filep, orig_pos - 5);
819 	if (ret) {
820 		TC_PRINT("fs_truncate failed [%d]\n", ret);
821 		fs_close(&filep);
822 		return ret;
823 	}
824 
825 	fs_seek(&filep, 0, FS_SEEK_END);
826 	TC_PRINT("File size after shrinking by 5 bytes = %d\n",
827 						(int)fs_tell(&filep));
828 	if (fs_tell(&filep) != (orig_pos - 5)) {
829 		TC_PRINT("File size after fs_truncate not as expected\n");
830 		fs_close(&filep);
831 		return TC_FAIL;
832 	}
833 
834 	/* Test expanding file */
835 	TC_PRINT("\nTesting expanding\n");
836 	fs_seek(&filep, 0, FS_SEEK_END);
837 	orig_pos = fs_tell(&filep);
838 	ret = fs_truncate(&filep, orig_pos + 10);
839 	if (ret) {
840 		TC_PRINT("fs_truncate failed [%d]\n", ret);
841 		fs_close(&filep);
842 		return ret;
843 	}
844 
845 	fs_seek(&filep, 0, FS_SEEK_END);
846 	TC_PRINT("File size after expanding by 10 bytes = %d\n",
847 						(int)fs_tell(&filep));
848 	if (fs_tell(&filep) != (orig_pos + 10)) {
849 		TC_PRINT("File size after fs_truncate not as expected\n");
850 		fs_close(&filep);
851 		return TC_FAIL;
852 	}
853 
854 	/* Check if expanded regions are zeroed */
855 	TC_PRINT("Testing for zeroes in expanded region\n");
856 	fs_seek(&filep, -5, FS_SEEK_END);
857 
858 	brw = fs_read(&filep, read_buff, 5);
859 
860 	if (brw < 5) {
861 		TC_PRINT("Read failed after truncating\n");
862 		fs_close(&filep);
863 		return -1;
864 	}
865 
866 	for (int i = 0; i < 5; i++) {
867 		if (read_buff[i]) {
868 			TC_PRINT("Expanded regions are not zeroed\n");
869 			fs_close(&filep);
870 			return TC_FAIL;
871 		}
872 	}
873 
874 	return TC_PASS;
875 }
876 
877 /**
878  * @brief Truncate the file to the new length and check
879  *
880  * @details
881  * - fs_seek(), locate the position to truncate
882  * - fs_truncate()
883  * - fs_tell(), retrieve the current position
884  *
885  * @ingroup filesystem_api
886  */
test_file_truncate(void)887 void test_file_truncate(void)
888 {
889 	zassert_true(_test_file_truncate() == TC_PASS);
890 }
891 
892 /**
893  * @brief Test close file interface in file system core
894  *
895  * @ingroup filesystem_api
896  */
test_file_close(void)897 void test_file_close(void)
898 {
899 	int ret;
900 
901 	TC_PRINT("\nClose tests:\n");
902 
903 	TC_PRINT("Close an unopened file\n");
904 	fs_file_t_init(&err_filep);
905 	ret = fs_close(&err_filep);
906 	zassert_equal(ret, 0, "Should close an unopened file");
907 
908 	TC_PRINT("Filesystem has no close interface\n");
909 	err_filep.mp = &test_fs_mnt_no_op;
910 	ret = fs_close(&err_filep);
911 	zassert_not_equal(ret, 0, "Filesystem has no close interface");
912 
913 	ret = fs_close(&filep);
914 	zassert_equal(ret, 0, "Fail to close file");
915 
916 	TC_PRINT("Reuse fs_file_t from closed file");
917 	ret = fs_open(&filep, TEST_FILE, FS_O_READ);
918 	zassert_equal(ret, 0, "Expected open to succeed, got %d", ret);
919 	ret = fs_close(&filep);
920 	zassert_equal(ret, 0, "Expected close to succeed, got %d", ret);
921 
922 	TC_PRINT("\nClose a closed file:\n");
923 	filep.mp = &test_fs_mnt_1;
924 	ret = fs_close(&filep);
925 	zassert_not_equal(ret, 0, "Should not reclose a closed file");
926 
927 	TC_PRINT("Closed file %s\n", TEST_FILE);
928 }
929 
930 /**
931  * @brief Test fs_rename() interface in file system core
932  *
933  * @ingroup filesystem_api
934  */
ZTEST(fs_api_dir_file,test_file_rename)935 ZTEST(fs_api_dir_file, test_file_rename)
936 {
937 	int ret = TC_FAIL;
938 
939 	TC_PRINT("\nRename file tests:\n");
940 
941 	ret = fs_rename(NULL, NULL);
942 	zassert_not_equal(ret, 0, "Rename a NULL file");
943 
944 	ret = fs_rename("/", TEST_FILE_RN);
945 	zassert_not_equal(ret, 0, "source file name is too short");
946 
947 	ret = fs_rename("testfile.txt", TEST_FILE_RN);
948 	zassert_not_equal(ret, 0, "source file name doesn't start with /");
949 
950 	ret = fs_rename("/SDCARD:/testfile.txt", NULL);
951 	zassert_not_equal(ret, 0, "Rename to a NULL file");
952 
953 	ret = fs_rename("/SDCARD:/testfile.txt", "/");
954 	zassert_not_equal(ret, 0, "dest file name too short");
955 
956 	ret = fs_rename("/SDCARD:/testfile.txt", "rename.txt");
957 	zassert_not_equal(ret, 0, "dest file name doesn't start with /");
958 
959 	ret = fs_rename("/SDCARD:/testfile.txt", TEST_FILE_RN);
960 	zassert_not_equal(ret, 0, "Rename a not existing file");
961 
962 	ret = fs_rename(TEST_FILE, "/SDCARD:/testfile_renamed.txt");
963 	zassert_not_equal(ret, 0, "Rename file to different mount point");
964 
965 	ret = fs_rename(TEST_FILE, TEST_FILE_EX);
966 	zassert_not_equal(ret, 0, "Rename file to an exist file");
967 
968 	ret = fs_rename(NOOP_MNTP"/test.txt", NOOP_MNTP"/test_new.txt");
969 	zassert_not_equal(ret, 0, "Filesystem has no rename functionality");
970 
971 	ret = fs_rename(TEST_FILE, TEST_FILE_RN);
972 	zassert_equal(ret, 0, "Fail to rename a file");
973 }
974 
975 /**
976  * @brief Test fs_stat() interface in filesystem core
977  *
978  * @ingroup filesystem_api
979  */
ZTEST(fs_api_dir_file,test_file_stat)980 ZTEST(fs_api_dir_file, test_file_stat)
981 {
982 	int ret;
983 	struct fs_dirent entry;
984 
985 	TC_PRINT("\nStat file tests:\n");
986 
987 	ret = fs_stat(NULL, &entry);
988 	zassert_not_equal(ret, 0, "Pointer to path is NULL");
989 
990 	ret = fs_stat(TEST_DIR, NULL);
991 	zassert_not_equal(ret, 0, "Stat a dir without entry");
992 
993 	ret = fs_stat("/", &entry);
994 	zassert_not_equal(ret, 0, "dir path name is too short");
995 
996 	ret = fs_stat("SDCARD", &entry);
997 	zassert_not_equal(ret, 0, "Stat a dir path without /");
998 
999 	ret = fs_stat("/SDCARD", &entry);
1000 	zassert_not_equal(ret, 0, "Stat a not existing dir");
1001 
1002 	ret = fs_stat(NOOP_MNTP, &entry);
1003 	zassert_not_equal(ret, 0, "filesystem has no stat functionality");
1004 
1005 	ret = fs_stat(TEST_DIR, &entry);
1006 	zassert_equal(ret, 0, "Fail to stat a dir");
1007 
1008 	ret = fs_stat(TEST_DIR_FILE, &entry);
1009 	zassert_equal(ret, 0, "Fail to stat a file");
1010 }
1011 
1012 /**
1013  * @brief Test fs_unlink() interface in filesystem core
1014  *
1015  * @ingroup filesystem_api
1016  */
ZTEST(fs_api_dir_file,test_file_unlink)1017 ZTEST(fs_api_dir_file, test_file_unlink)
1018 {
1019 	int ret;
1020 
1021 	TC_PRINT("\nDelete tests:\n");
1022 
1023 	ret = fs_unlink(NULL);
1024 	zassert_not_equal(ret, 0, "Delete a NULL file");
1025 
1026 	ret = fs_unlink("/");
1027 	zassert_not_equal(ret, 0, "Delete a file with too short name");
1028 
1029 	ret = fs_unlink("SDCARD:/test_file.txt");
1030 	zassert_not_equal(ret, 0, "Delete a file with missing root / in path");
1031 
1032 	ret = fs_unlink("/SDCARD:/test_file.txt");
1033 	zassert_not_equal(ret, 0, "Delete a not existing file");
1034 
1035 	ret = fs_unlink(TEST_FS_MNTP);
1036 	zassert_not_equal(ret, 0, "Delete a root dir");
1037 
1038 	ret = fs_unlink(NOOP_MNTP);
1039 	zassert_not_equal(ret, 0, "Filesystem has no unlink functionality");
1040 
1041 	/* In filesystem core, static function fs_get_mnt_point() check
1042 	 * the length of mount point's name. It is not an API, test this
1043 	 * function at this point because test_file_unlink() is the
1044 	 * last test case before test_unmount(), change mountp_len of
1045 	 * test_fs_mnt_no_op to 0 could not effect other test cases
1046 	 */
1047 	test_fs_mnt_no_op.mountp_len = 0;
1048 	ret = fs_unlink(NOOP_MNTP);
1049 	zassert_not_equal(ret, 0, "mount point with 0 mountp_len can't be get");
1050 
1051 	ret = fs_unlink(TEST_FILE_RN);
1052 	zassert_equal(ret, 0, "Fail to delete file");
1053 
1054 	TC_PRINT("File (%s) deleted successfully!\n", TEST_FILE_RN);
1055 }
1056 
fs_api_setup(void)1057 static void *fs_api_setup(void)
1058 {
1059 	fs_register(TEST_FS_1, &temp_fs);
1060 	fs_mount(&test_fs_mnt_1);
1061 	memset(&null_fs, 0, sizeof(null_fs));
1062 	null_fs.mount = temp_fs.mount;
1063 	null_fs.unmount = temp_fs.unmount;
1064 	fs_register(TEST_FS_2, &null_fs);
1065 	fs_mount(&test_fs_mnt_no_op);
1066 	return NULL;
1067 }
1068 
fs_api_teardown(void * fixtrue)1069 static void fs_api_teardown(void *fixtrue)
1070 {
1071 	fs_unmount(&test_fs_mnt_no_op);
1072 	fs_unregister(TEST_FS_2, &null_fs);
1073 	fs_unmount(&test_fs_mnt_1);
1074 	fs_unregister(TEST_FS_1, &temp_fs);
1075 }
1076 
ZTEST(fs_api_dir_file,test_fs_dir)1077 ZTEST(fs_api_dir_file, test_fs_dir)
1078 {
1079 	test_mkdir();
1080 	test_opendir();
1081 	test_closedir();
1082 	test_opendir_closedir();
1083 	test_lsdir();
1084 }
1085 
ZTEST(fs_api_dir_file,test_file_ops)1086 ZTEST(fs_api_dir_file, test_file_ops)
1087 {
1088 	test_file_open();
1089 	test_file_write();
1090 	test_file_read();
1091 	test_file_seek();
1092 	test_file_truncate();
1093 	test_file_close();
1094 }
1095 
ZTEST(fs_api_register_mount,test_mount_unmount)1096 ZTEST(fs_api_register_mount, test_mount_unmount)
1097 {
1098 	fs_register(TEST_FS_1, &temp_fs);
1099 	test_mount();
1100 	test_unmount();
1101 	fs_unregister(TEST_FS_1, &temp_fs);
1102 }
1103 
1104 ZTEST_SUITE(fs_api_register_mount, NULL, NULL, NULL, NULL, NULL);
1105 ZTEST_SUITE(fs_api_dir_file, NULL, fs_api_setup, NULL, NULL, fs_api_teardown);
1106