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