| 
									
										
										
										
											2017-06-21 01:21:14 +08:00
										 |  |  | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <sys/fcntl.h>
 | 
					
						
							|  |  |  | #include <sys/dirent.h>
 | 
					
						
							|  |  |  | #include "esp_vfs.h"
 | 
					
						
							|  |  |  | #include "unity.h"
 | 
					
						
							|  |  |  | #include "esp_log.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Dummy VFS implementation to check if VFS is called or not with expected path
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |     const char* match_path; | 
					
						
							|  |  |  |     bool called; | 
					
						
							|  |  |  | } dummy_vfs_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int dummy_open(void* ctx, const char * path, int flags, int mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; | 
					
						
							|  |  |  |     dummy->called = true; | 
					
						
							|  |  |  |     if (strcmp(dummy->match_path, path) == 0) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     errno = ENOENT; | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int dummy_close(void* ctx, int fd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; | 
					
						
							|  |  |  |     dummy->called = true; | 
					
						
							|  |  |  |     if (fd == 1) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     errno = EBADF; | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static DIR* dummy_opendir(void* ctx, const char* path) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; | 
					
						
							|  |  |  |     dummy->called = true; | 
					
						
							|  |  |  |     if (strcmp(dummy->match_path, path) == 0) { | 
					
						
							|  |  |  |         DIR* result = calloc(1, sizeof(DIR)); | 
					
						
							|  |  |  |         TEST_ASSERT_NOT_NULL(result); | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     errno = ENOENT; | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int dummy_closedir(void* ctx, DIR* pdir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; | 
					
						
							|  |  |  |     dummy->called = true; | 
					
						
							|  |  |  |     free(pdir); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initializer for this dummy VFS implementation
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DUMMY_VFS() { \
 | 
					
						
							|  |  |  |         .flags = ESP_VFS_FLAG_CONTEXT_PTR, \ | 
					
						
							|  |  |  |         .open_p = dummy_open, \ | 
					
						
							|  |  |  |         .close_p = dummy_close, \ | 
					
						
							|  |  |  |         .opendir_p = dummy_opendir, \ | 
					
						
							|  |  |  |         .closedir_p = dummy_closedir \ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper functions to test VFS behavior
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void test_open(dummy_vfs_t* instance, const char* path, | 
					
						
							|  |  |  |         bool should_be_called, bool should_be_opened, int line) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const int flags = O_CREAT | O_TRUNC | O_RDWR; | 
					
						
							|  |  |  |     instance->called = false; | 
					
						
							|  |  |  |     int fd = esp_vfs_open(__getreent(), path, flags, 0); | 
					
						
							|  |  |  |     UNITY_TEST_ASSERT_EQUAL_INT(should_be_called, instance->called, line, | 
					
						
							|  |  |  |             "should_be_called check failed"); | 
					
						
							|  |  |  |     if (should_be_called) { | 
					
						
							|  |  |  |         if (should_be_opened) { | 
					
						
							|  |  |  |             UNITY_TEST_ASSERT(fd >= 0, line, "should be opened"); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             UNITY_TEST_ASSERT(fd < 0, line, "should not be opened"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     esp_vfs_close(__getreent(), fd); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void test_opendir(dummy_vfs_t* instance, const char* path, | 
					
						
							|  |  |  |         bool should_be_called, bool should_be_opened, int line) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     instance->called = false; | 
					
						
							|  |  |  |     DIR* dir = opendir(path); | 
					
						
							|  |  |  |     UNITY_TEST_ASSERT_EQUAL_INT(should_be_called, instance->called, line, | 
					
						
							|  |  |  |             "should_be_called check failed"); | 
					
						
							|  |  |  |     if (should_be_called) { | 
					
						
							|  |  |  |         if (should_be_opened) { | 
					
						
							|  |  |  |             UNITY_TEST_ASSERT(dir != NULL, line, "should be opened"); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             UNITY_TEST_ASSERT(dir == 0, line, "should not be opened"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (dir) { | 
					
						
							|  |  |  |         closedir(dir); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper macros which forward line number to assertion macros inside test_open
 | 
					
						
							|  |  |  |  * and test_opendir | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define test_opened(instance, path) test_open(instance, path, true, true, __LINE__)
 | 
					
						
							|  |  |  | #define test_not_opened(instance, path) test_open(instance, path, true, false, __LINE__)
 | 
					
						
							|  |  |  | #define test_not_called(instance, path) test_open(instance, path, false, false, __LINE__)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define test_dir_opened(instance, path) test_opendir(instance, path, true, true, __LINE__)
 | 
					
						
							|  |  |  | #define test_dir_not_opened(instance, path) test_opendir(instance, path, true, false, __LINE__)
 | 
					
						
							|  |  |  | #define test_dir_not_called(instance, path) test_opendir(instance, path, false, false, __LINE__)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TEST_CASE("vfs parses paths correctly", "[vfs]") | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t inst_foo = { | 
					
						
							|  |  |  |             .match_path = "", | 
					
						
							|  |  |  |             .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_foo = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dummy_vfs_t inst_foo1 = { | 
					
						
							|  |  |  |         .match_path = "", | 
					
						
							|  |  |  |         .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_foo1 = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo1", &desc_foo1, &inst_foo1) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     inst_foo.match_path = "/file"; | 
					
						
							|  |  |  |     test_opened(&inst_foo, "/foo/file"); | 
					
						
							|  |  |  |     test_not_opened(&inst_foo, "/foo/file1"); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo1/file"); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo1"); | 
					
						
							|  |  |  |     test_not_opened(&inst_foo, "/foo"); | 
					
						
							|  |  |  |     inst_foo.match_path = "/junk"; | 
					
						
							|  |  |  |     test_dir_opened(&inst_foo, "/foo/junk"); | 
					
						
							|  |  |  |     inst_foo.match_path = "/"; | 
					
						
							|  |  |  |     test_dir_opened(&inst_foo, "/foo/"); | 
					
						
							|  |  |  |     test_dir_opened(&inst_foo, "/foo"); | 
					
						
							|  |  |  |     test_dir_not_called(&inst_foo1, "/foo"); | 
					
						
							|  |  |  |     test_dir_not_opened(&inst_foo, "/foo/1"); | 
					
						
							|  |  |  |     test_dir_not_called(&inst_foo, "/foo1"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     inst_foo1.match_path = "/file1"; | 
					
						
							|  |  |  |     test_not_called(&inst_foo1, "/foo/file1"); | 
					
						
							|  |  |  |     test_opened(&inst_foo1, "/foo1/file1"); | 
					
						
							|  |  |  |     test_not_opened(&inst_foo1, "/foo1/file"); | 
					
						
							| 
									
										
										
										
											2017-06-21 14:17:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Test nested VFS entries
 | 
					
						
							|  |  |  |     dummy_vfs_t inst_foobar = { | 
					
						
							|  |  |  |         .match_path = "", | 
					
						
							|  |  |  |         .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_foobar = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dummy_vfs_t inst_toplevel = { | 
					
						
							|  |  |  |         .match_path = "", | 
					
						
							|  |  |  |         .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_toplevel = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("", &desc_toplevel, &inst_toplevel) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     inst_foo.match_path = "/bar/file"; | 
					
						
							|  |  |  |     inst_foobar.match_path = "/file"; | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_dir_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_dir_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  |     inst_toplevel.match_path = "/tmp/foo"; | 
					
						
							|  |  |  |     test_opened(&inst_toplevel, "/tmp/foo"); | 
					
						
							| 
									
										
										
										
											2017-06-23 13:07:43 +01:00
										 |  |  |     inst_toplevel.match_path = "foo"; | 
					
						
							|  |  |  |     test_opened(&inst_toplevel, "foo"); | 
					
						
							| 
									
										
										
										
											2017-06-21 14:17:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-21 13:54:04 +08:00
										 |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo") ); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo1") ); | 
					
						
							| 
									
										
										
										
											2017-06-21 14:17:14 +08:00
										 |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("") ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-06 19:30:05 +08:00
										 |  |  | TEST_CASE("vfs unregisters correct nested mount point", "[vfs]") | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t inst_foobar = { | 
					
						
							|  |  |  |         .match_path = "/file", | 
					
						
							|  |  |  |         .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_foobar = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dummy_vfs_t inst_foo = { | 
					
						
							|  |  |  |         .match_path = "/bar/file", | 
					
						
							|  |  |  |         .called = false | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_vfs_t desc_foo = DUMMY_VFS(); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* basic operation */ | 
					
						
							|  |  |  |     test_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* this should not match anything */ | 
					
						
							|  |  |  |     TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_vfs_unregister("/foo/b")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* unregister "/foo" and check that we haven't unregistered "/foo/bar" */ | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo") ); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* repeat the above, with the reverse order of registration */ | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); | 
					
						
							|  |  |  |     test_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo") ); | 
					
						
							|  |  |  |     test_not_called(&inst_foo, "/foo/bar/file"); | 
					
						
							|  |  |  |     test_opened(&inst_foobar, "/foo/bar/file"); | 
					
						
							|  |  |  |     TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-21 14:17:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | void test_vfs_register(const char* prefix, bool expect_success, int line) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dummy_vfs_t inst; | 
					
						
							|  |  |  |     esp_vfs_t desc = DUMMY_VFS(); | 
					
						
							|  |  |  |     esp_err_t err = esp_vfs_register(prefix, &desc, &inst); | 
					
						
							|  |  |  |     if (expect_success) { | 
					
						
							|  |  |  |         UNITY_TEST_ASSERT_EQUAL_INT(ESP_OK, err, line, "esp_vfs_register should succeed"); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         UNITY_TEST_ASSERT_EQUAL_INT(ESP_ERR_INVALID_ARG, | 
					
						
							|  |  |  |                 err, line, "esp_vfs_register should fail"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (err == ESP_OK) { | 
					
						
							|  |  |  |         TEST_ESP_OK( esp_vfs_unregister(prefix) ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define test_register_ok(prefix) test_vfs_register(prefix, true, __LINE__)
 | 
					
						
							|  |  |  | #define test_register_fail(prefix) test_vfs_register(prefix, false, __LINE__)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TEST_CASE("vfs checks mount point path", "[vfs]") | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     test_register_ok(""); | 
					
						
							|  |  |  |     test_register_fail("/"); | 
					
						
							|  |  |  |     test_register_fail("a"); | 
					
						
							|  |  |  |     test_register_fail("aa"); | 
					
						
							|  |  |  |     test_register_fail("aaa"); | 
					
						
							|  |  |  |     test_register_ok("/a"); | 
					
						
							|  |  |  |     test_register_ok("/aa"); | 
					
						
							|  |  |  |     test_register_ok("/aaa/bbb"); | 
					
						
							|  |  |  |     test_register_fail("/aaa/"); | 
					
						
							|  |  |  |     test_register_fail("/aaa/bbb/"); | 
					
						
							|  |  |  |     test_register_ok("/23456789012345"); | 
					
						
							|  |  |  |     test_register_fail("/234567890123456"); | 
					
						
							| 
									
										
										
										
											2017-06-21 01:21:14 +08:00
										 |  |  | } |