test_vfs_paths.c 8.4 KB

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