// 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 #include #include #include #include #include #include #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"); // 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"); inst_toplevel.match_path = "foo"; test_opened(&inst_toplevel, "foo"); TEST_ESP_OK( esp_vfs_unregister("/foo") ); TEST_ESP_OK( esp_vfs_unregister("/foo1") ); TEST_ESP_OK( esp_vfs_unregister("/foo/bar") ); TEST_ESP_OK( esp_vfs_unregister("") ); } 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"); }