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