/ components / vfs / test / test_vfs_paths.c
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  }