test_vfs_paths.c
1 // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <stdbool.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <errno.h> 20 #include <sys/fcntl.h> 21 #include <sys/dirent.h> 22 #include "esp_vfs.h" 23 #include "unity.h" 24 #include "esp_log.h" 25 26 /* Dummy VFS implementation to check if VFS is called or not with expected path 27 */ 28 typedef struct { 29 const char* match_path; 30 bool called; 31 } dummy_vfs_t; 32 33 static int dummy_open(void* ctx, const char * path, int flags, int mode) 34 { 35 dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; 36 dummy->called = true; 37 if (strcmp(dummy->match_path, path) == 0) { 38 return 1; 39 } 40 errno = ENOENT; 41 return -1; 42 } 43 44 static int dummy_close(void* ctx, int fd) 45 { 46 dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; 47 dummy->called = true; 48 if (fd == 1) { 49 return 0; 50 } 51 errno = EBADF; 52 return -1; 53 } 54 55 static DIR* dummy_opendir(void* ctx, const char* path) 56 { 57 dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; 58 dummy->called = true; 59 if (strcmp(dummy->match_path, path) == 0) { 60 DIR* result = calloc(1, sizeof(DIR)); 61 TEST_ASSERT_NOT_NULL(result); 62 return result; 63 } 64 errno = ENOENT; 65 return NULL; 66 } 67 68 static int dummy_closedir(void* ctx, DIR* pdir) 69 { 70 dummy_vfs_t* dummy = (dummy_vfs_t*) ctx; 71 dummy->called = true; 72 free(pdir); 73 return 0; 74 } 75 76 /* Initializer for this dummy VFS implementation 77 */ 78 79 #define DUMMY_VFS() { \ 80 .flags = ESP_VFS_FLAG_CONTEXT_PTR, \ 81 .open_p = dummy_open, \ 82 .close_p = dummy_close, \ 83 .opendir_p = dummy_opendir, \ 84 .closedir_p = dummy_closedir \ 85 } 86 87 /* Helper functions to test VFS behavior 88 */ 89 90 static void test_open(dummy_vfs_t* instance, const char* path, 91 bool should_be_called, bool should_be_opened, int line) 92 { 93 const int flags = O_CREAT | O_TRUNC | O_RDWR; 94 instance->called = false; 95 int fd = esp_vfs_open(__getreent(), path, flags, 0); 96 UNITY_TEST_ASSERT_EQUAL_INT(should_be_called, instance->called, line, 97 "should_be_called check failed"); 98 if (should_be_called) { 99 if (should_be_opened) { 100 UNITY_TEST_ASSERT(fd >= 0, line, "should be opened"); 101 } else { 102 UNITY_TEST_ASSERT(fd < 0, line, "should not be opened"); 103 } 104 } 105 esp_vfs_close(__getreent(), fd); 106 } 107 108 static void test_opendir(dummy_vfs_t* instance, const char* path, 109 bool should_be_called, bool should_be_opened, int line) 110 { 111 instance->called = false; 112 DIR* dir = opendir(path); 113 UNITY_TEST_ASSERT_EQUAL_INT(should_be_called, instance->called, line, 114 "should_be_called check failed"); 115 if (should_be_called) { 116 if (should_be_opened) { 117 UNITY_TEST_ASSERT(dir != NULL, line, "should be opened"); 118 } else { 119 UNITY_TEST_ASSERT(dir == 0, line, "should not be opened"); 120 } 121 } 122 if (dir) { 123 closedir(dir); 124 } 125 } 126 127 /* Helper macros which forward line number to assertion macros inside test_open 128 * and test_opendir 129 */ 130 131 #define test_opened(instance, path) test_open(instance, path, true, true, __LINE__) 132 #define test_not_opened(instance, path) test_open(instance, path, true, false, __LINE__) 133 #define test_not_called(instance, path) test_open(instance, path, false, false, __LINE__) 134 135 #define test_dir_opened(instance, path) test_opendir(instance, path, true, true, __LINE__) 136 #define test_dir_not_opened(instance, path) test_opendir(instance, path, true, false, __LINE__) 137 #define test_dir_not_called(instance, path) test_opendir(instance, path, false, false, __LINE__) 138 139 140 141 TEST_CASE("vfs parses paths correctly", "[vfs]") 142 { 143 dummy_vfs_t inst_foo = { 144 .match_path = "", 145 .called = false 146 }; 147 esp_vfs_t desc_foo = DUMMY_VFS(); 148 TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); 149 150 dummy_vfs_t inst_foo1 = { 151 .match_path = "", 152 .called = false 153 }; 154 esp_vfs_t desc_foo1 = DUMMY_VFS(); 155 TEST_ESP_OK( esp_vfs_register("/foo1", &desc_foo1, &inst_foo1) ); 156 157 inst_foo.match_path = "/file"; 158 test_opened(&inst_foo, "/foo/file"); 159 test_not_opened(&inst_foo, "/foo/file1"); 160 test_not_called(&inst_foo, "/foo1/file"); 161 test_not_called(&inst_foo, "/foo1"); 162 test_not_opened(&inst_foo, "/foo"); 163 inst_foo.match_path = "/junk"; 164 test_dir_opened(&inst_foo, "/foo/junk"); 165 inst_foo.match_path = "/"; 166 test_dir_opened(&inst_foo, "/foo/"); 167 test_dir_opened(&inst_foo, "/foo"); 168 test_dir_not_called(&inst_foo1, "/foo"); 169 test_dir_not_opened(&inst_foo, "/foo/1"); 170 test_dir_not_called(&inst_foo, "/foo1"); 171 172 inst_foo1.match_path = "/file1"; 173 test_not_called(&inst_foo1, "/foo/file1"); 174 test_opened(&inst_foo1, "/foo1/file1"); 175 test_not_opened(&inst_foo1, "/foo1/file"); 176 177 // Test nested VFS entries 178 dummy_vfs_t inst_foobar = { 179 .match_path = "", 180 .called = false 181 }; 182 esp_vfs_t desc_foobar = DUMMY_VFS(); 183 TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); 184 185 dummy_vfs_t inst_toplevel = { 186 .match_path = "", 187 .called = false 188 }; 189 esp_vfs_t desc_toplevel = DUMMY_VFS(); 190 TEST_ESP_OK( esp_vfs_register("", &desc_toplevel, &inst_toplevel) ); 191 192 inst_foo.match_path = "/bar/file"; 193 inst_foobar.match_path = "/file"; 194 test_not_called(&inst_foo, "/foo/bar/file"); 195 test_opened(&inst_foobar, "/foo/bar/file"); 196 test_dir_not_called(&inst_foo, "/foo/bar/file"); 197 test_dir_opened(&inst_foobar, "/foo/bar/file"); 198 inst_toplevel.match_path = "/tmp/foo"; 199 test_opened(&inst_toplevel, "/tmp/foo"); 200 inst_toplevel.match_path = "foo"; 201 test_opened(&inst_toplevel, "foo"); 202 203 TEST_ESP_OK( esp_vfs_unregister("/foo") ); 204 TEST_ESP_OK( esp_vfs_unregister("/foo1") ); 205 TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); 206 TEST_ESP_OK( esp_vfs_unregister("") ); 207 } 208 209 TEST_CASE("vfs unregisters correct nested mount point", "[vfs]") 210 { 211 dummy_vfs_t inst_foobar = { 212 .match_path = "/file", 213 .called = false 214 }; 215 esp_vfs_t desc_foobar = DUMMY_VFS(); 216 TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); 217 218 dummy_vfs_t inst_foo = { 219 .match_path = "/bar/file", 220 .called = false 221 }; 222 esp_vfs_t desc_foo = DUMMY_VFS(); 223 TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); 224 225 /* basic operation */ 226 test_opened(&inst_foobar, "/foo/bar/file"); 227 test_not_called(&inst_foo, "/foo/bar/file"); 228 229 /* this should not match anything */ 230 TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_vfs_unregister("/foo/b")); 231 232 /* unregister "/foo" and check that we haven't unregistered "/foo/bar" */ 233 TEST_ESP_OK( esp_vfs_unregister("/foo") ); 234 test_not_called(&inst_foo, "/foo/bar/file"); 235 test_opened(&inst_foobar, "/foo/bar/file"); 236 237 /* repeat the above, with the reverse order of registration */ 238 TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); 239 TEST_ESP_OK( esp_vfs_register("/foo", &desc_foo, &inst_foo) ); 240 TEST_ESP_OK( esp_vfs_register("/foo/bar", &desc_foobar, &inst_foobar) ); 241 test_opened(&inst_foobar, "/foo/bar/file"); 242 test_not_called(&inst_foo, "/foo/bar/file"); 243 TEST_ESP_OK( esp_vfs_unregister("/foo") ); 244 test_not_called(&inst_foo, "/foo/bar/file"); 245 test_opened(&inst_foobar, "/foo/bar/file"); 246 TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); 247 } 248 249 250 void test_vfs_register(const char* prefix, bool expect_success, int line) 251 { 252 dummy_vfs_t inst; 253 esp_vfs_t desc = DUMMY_VFS(); 254 esp_err_t err = esp_vfs_register(prefix, &desc, &inst); 255 if (expect_success) { 256 UNITY_TEST_ASSERT_EQUAL_INT(ESP_OK, err, line, "esp_vfs_register should succeed"); 257 } else { 258 UNITY_TEST_ASSERT_EQUAL_INT(ESP_ERR_INVALID_ARG, 259 err, line, "esp_vfs_register should fail"); 260 } 261 if (err == ESP_OK) { 262 TEST_ESP_OK( esp_vfs_unregister(prefix) ); 263 } 264 } 265 266 #define test_register_ok(prefix) test_vfs_register(prefix, true, __LINE__) 267 #define test_register_fail(prefix) test_vfs_register(prefix, false, __LINE__) 268 269 TEST_CASE("vfs checks mount point path", "[vfs]") 270 { 271 test_register_ok(""); 272 test_register_fail("/"); 273 test_register_fail("a"); 274 test_register_fail("aa"); 275 test_register_fail("aaa"); 276 test_register_ok("/a"); 277 test_register_ok("/aa"); 278 test_register_ok("/aaa/bbb"); 279 test_register_fail("/aaa/"); 280 test_register_fail("/aaa/bbb/"); 281 test_register_ok("/23456789012345"); 282 test_register_fail("/234567890123456"); 283 }