/ components / spiffs / test / test_spiffs.c
test_spiffs.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 <stdio.h>
 16  #include <stdlib.h>
 17  #include <string.h>
 18  #include <time.h>
 19  #include <sys/time.h>
 20  #include <sys/unistd.h>
 21  #include "unity.h"
 22  #include "test_utils.h"
 23  #include "esp_log.h"
 24  #include "esp_system.h"
 25  #include "esp_vfs.h"
 26  #include "esp_spiffs.h"
 27  #include "freertos/FreeRTOS.h"
 28  #include "freertos/task.h"
 29  #include "freertos/queue.h"
 30  #include "freertos/semphr.h"
 31  #include "esp_partition.h"
 32  #include "esp_rom_sys.h"
 33  
 34  const char* spiffs_test_hello_str = "Hello, World!\n";
 35  const char* spiffs_test_partition_label = "flash_test";
 36  
 37  void test_spiffs_create_file_with_text(const char* name, const char* text)
 38  {
 39      FILE* f = fopen(name, "wb");
 40      TEST_ASSERT_NOT_NULL(f);
 41      TEST_ASSERT_TRUE(fputs(text, f) != EOF);
 42      TEST_ASSERT_EQUAL(0, fclose(f));
 43  }
 44  
 45  void test_spiffs_overwrite_append(const char* filename)
 46  {
 47      /* Create new file with 'aaaa' */
 48      test_spiffs_create_file_with_text(filename, "aaaa");
 49  
 50      /* Append 'bbbb' to file */
 51      FILE *f_a = fopen(filename, "a");
 52      TEST_ASSERT_NOT_NULL(f_a);
 53      TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
 54      TEST_ASSERT_EQUAL(0, fclose(f_a));
 55  
 56      /* Read back 8 bytes from file, verify it's 'aaaabbbb' */
 57      char buf[10] = { 0 };
 58      FILE *f_r = fopen(filename, "r");
 59      TEST_ASSERT_NOT_NULL(f_r);
 60      TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
 61      TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
 62  
 63      /* Be sure we're at end of file */
 64      TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
 65  
 66      TEST_ASSERT_EQUAL(0, fclose(f_r));
 67  
 68      /* Overwrite file with 'cccc' */
 69      test_spiffs_create_file_with_text(filename, "cccc");
 70  
 71      /* Verify file now only contains 'cccc' */
 72      f_r = fopen(filename, "r");
 73      TEST_ASSERT_NOT_NULL(f_r);
 74      bzero(buf, sizeof(buf));
 75      TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
 76      TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
 77      TEST_ASSERT_EQUAL(0, fclose(f_r));
 78  }
 79  
 80  void test_spiffs_read_file(const char* filename)
 81  {
 82      FILE* f = fopen(filename, "r");
 83      TEST_ASSERT_NOT_NULL(f);
 84      char buf[32] = { 0 };
 85      int cb = fread(buf, 1, sizeof(buf), f);
 86      TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb);
 87      TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf));
 88      TEST_ASSERT_EQUAL(0, fclose(f));
 89  }
 90  
 91  void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count)
 92  {
 93      FILE** files = calloc(files_count, sizeof(FILE*));
 94      for (size_t i = 0; i < files_count; ++i) {
 95          char name[32];
 96          snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
 97          files[i] = fopen(name, "w");
 98          TEST_ASSERT_NOT_NULL(files[i]);
 99      }
100      /* close everything and clean up */
101      for (size_t i = 0; i < files_count; ++i) {
102          fclose(files[i]);
103      }
104      free(files);
105  }
106  
107  void test_spiffs_lseek(const char* filename)
108  {
109      FILE* f = fopen(filename, "wb+");
110      TEST_ASSERT_NOT_NULL(f);
111      TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
112      TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
113      TEST_ASSERT_EQUAL('9', fgetc(f));
114      TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
115      TEST_ASSERT_EQUAL('3', fgetc(f));
116      TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
117      TEST_ASSERT_EQUAL('8', fgetc(f));
118      TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
119      TEST_ASSERT_EQUAL(11, ftell(f));
120      TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
121      TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
122      TEST_ASSERT_EQUAL(15, ftell(f));
123      TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
124      char buf[20];
125      TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f));
126      const char ref_buf[] = "0123456789\nabc\n";
127      TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
128  
129      TEST_ASSERT_EQUAL(0, fclose(f));
130  }
131  
132  void test_spiffs_stat(const char* filename)
133  {
134      test_spiffs_create_file_with_text(filename, "foo\n");
135      struct stat st;
136      TEST_ASSERT_EQUAL(0, stat(filename, &st));
137      TEST_ASSERT(st.st_mode & S_IFREG);
138      TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
139  }
140  
141  void test_spiffs_unlink(const char* filename)
142  {
143      test_spiffs_create_file_with_text(filename, "unlink\n");
144  
145      TEST_ASSERT_EQUAL(0, unlink(filename));
146  
147      TEST_ASSERT_NULL(fopen(filename, "r"));
148  }
149  
150  void test_spiffs_rename(const char* filename_prefix)
151  {
152      char name_dst[64];
153      char name_src[64];
154      snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
155      snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
156  
157      unlink(name_dst);
158      unlink(name_src);
159  
160      FILE* f = fopen(name_src, "w+");
161      TEST_ASSERT_NOT_NULL(f);
162      const char* str = "0123456789";
163      for (int i = 0; i < 400; ++i) {
164          TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
165      }
166      TEST_ASSERT_EQUAL(0, fclose(f));
167      TEST_ASSERT_EQUAL(0, rename(name_src, name_dst));
168      TEST_ASSERT_NULL(fopen(name_src, "r"));
169      FILE* fdst = fopen(name_dst, "r");
170      TEST_ASSERT_NOT_NULL(fdst);
171      TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
172      TEST_ASSERT_EQUAL(4000, ftell(fdst));
173      TEST_ASSERT_EQUAL(0, fclose(fdst));
174  }
175  
176  void test_spiffs_can_opendir(const char* path)
177  {
178      char name_dir_file[64];
179      const char * file_name = "test_opd.txt";
180      snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
181      unlink(name_dir_file);
182      test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n");
183      DIR* dir = opendir(path);
184      TEST_ASSERT_NOT_NULL(dir);
185      bool found = false;
186      while (true) {
187          struct dirent* de = readdir(dir);
188          if (!de) {
189              break;
190          }
191          if (strcasecmp(de->d_name, file_name) == 0) {
192              found = true;
193              break;
194          }
195      }
196      TEST_ASSERT_TRUE(found);
197      TEST_ASSERT_EQUAL(0, closedir(dir));
198      unlink(name_dir_file);
199  }
200  
201  void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix)
202  {
203      char name_dir_inner_file[64];
204      char name_dir_inner[64];
205      char name_dir_file3[64];
206      char name_dir_file2[64];
207      char name_dir_file1[64];
208  
209      snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
210      snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
211      snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
212      snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
213      snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
214  
215      unlink(name_dir_inner_file);
216      rmdir(name_dir_inner);
217      unlink(name_dir_file1);
218      unlink(name_dir_file2);
219      unlink(name_dir_file3);
220      rmdir(dir_prefix);
221  
222      test_spiffs_create_file_with_text(name_dir_file1, "1\n");
223      test_spiffs_create_file_with_text(name_dir_file2, "2\n");
224      test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03");
225      test_spiffs_create_file_with_text(name_dir_inner_file, "3\n");
226  
227      DIR* dir = opendir(dir_prefix);
228      TEST_ASSERT_NOT_NULL(dir);
229      int count = 0;
230      const char* names[4];
231      while(count < 4) {
232          struct dirent* de = readdir(dir);
233          if (!de) {
234              break;
235          }
236          printf("found '%s'\n", de->d_name);
237          if (strcasecmp(de->d_name, "1.txt") == 0) {
238              TEST_ASSERT_TRUE(de->d_type == DT_REG);
239              names[count] = "1.txt";
240              ++count;
241          } else if (strcasecmp(de->d_name, "2.txt") == 0) {
242              TEST_ASSERT_TRUE(de->d_type == DT_REG);
243              names[count] = "2.txt";
244              ++count;
245          } else if (strcasecmp(de->d_name, "inner/3.txt") == 0) {
246              TEST_ASSERT_TRUE(de->d_type == DT_REG);
247              names[count] = "inner/3.txt";
248              ++count;
249          } else if (strcasecmp(de->d_name, "boo.bin") == 0) {
250              TEST_ASSERT_TRUE(de->d_type == DT_REG);
251              names[count] = "boo.bin";
252              ++count;
253          } else {
254              TEST_FAIL_MESSAGE("unexpected directory entry");
255          }
256      }
257      TEST_ASSERT_EQUAL(count, 4);
258  
259      rewinddir(dir);
260      struct dirent* de = readdir(dir);
261      TEST_ASSERT_NOT_NULL(de);
262      TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
263      seekdir(dir, 3);
264      de = readdir(dir);
265      TEST_ASSERT_NOT_NULL(de);
266      TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
267      seekdir(dir, 1);
268      de = readdir(dir);
269      TEST_ASSERT_NOT_NULL(de);
270      TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
271      seekdir(dir, 2);
272      de = readdir(dir);
273      TEST_ASSERT_NOT_NULL(de);
274      TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
275  
276      TEST_ASSERT_EQUAL(0, closedir(dir));
277  }
278  
279  void test_spiffs_readdir_many_files(const char* dir_prefix)
280  {
281      const int n_files = 40;
282      const int n_folders = 4;
283      unsigned char file_count[n_files * n_folders];
284      memset(file_count, 0, sizeof(file_count)/sizeof(file_count[0]));
285      char file_name[ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN];
286  
287      /* clean stale files before the test */
288      DIR* dir = opendir(dir_prefix);
289      if (dir) {
290          while (true) {
291              struct dirent* de = readdir(dir);
292              if (!de) {
293                  break;
294              }
295              int len = snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name);
296              assert(len < sizeof(file_name));
297              unlink(file_name);
298          }
299      }
300  
301      /* create files */
302      for (int d = 0; d < n_folders; ++d) {
303          printf("filling directory %d\n", d);
304          for (int f = 0; f < n_files; ++f) {
305              snprintf(file_name, sizeof(file_name), "%s/%d/%d.txt", dir_prefix, d, f);
306              test_spiffs_create_file_with_text(file_name, file_name);
307          }
308      }
309  
310      /* list files */
311      for (int d = 0; d < n_folders; ++d) {
312          printf("listing files in directory %d\n", d);
313          snprintf(file_name, sizeof(file_name), "%s/%d", dir_prefix, d);
314          dir = opendir(file_name);
315          TEST_ASSERT_NOT_NULL(dir);
316          while (true) {
317              struct dirent* de = readdir(dir);
318              if (!de) {
319                  break;
320              }
321              int file_id;
322              TEST_ASSERT_EQUAL(1, sscanf(de->d_name, "%d.txt", &file_id));
323              file_count[file_id + d * n_files]++;
324          }
325          closedir(dir);
326      }
327  
328      /* check that all created files have been seen */
329      for (int d = 0; d < n_folders; ++d) {
330          printf("checking that all files have been found in directory %d\n", d);
331          for (int f = 0; f < n_files; ++f) {
332              TEST_ASSERT_EQUAL(1, file_count[f + d * n_files]);
333          }
334      }
335  }
336  
337  
338  typedef struct {
339      const char* filename;
340      bool write;
341      size_t word_count;
342      int seed;
343      SemaphoreHandle_t done;
344      int result;
345  } read_write_test_arg_t;
346  
347  #define READ_WRITE_TEST_ARG_INIT(name, seed_) \
348          { \
349              .filename = name, \
350              .seed = seed_, \
351              .word_count = 4096, \
352              .write = true, \
353              .done = xSemaphoreCreateBinary() \
354          }
355  
356  static void read_write_task(void* param)
357  {
358      read_write_test_arg_t* args = (read_write_test_arg_t*) param;
359      FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
360      if (f == NULL) {
361          args->result = ESP_ERR_NOT_FOUND;
362          goto done;
363      }
364  
365      srand(args->seed);
366      for (size_t i = 0; i < args->word_count; ++i) {
367          uint32_t val = rand();
368          if (args->write) {
369              int cnt = fwrite(&val, sizeof(val), 1, f);
370              if (cnt != 1) {
371                  esp_rom_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
372                  args->result = ESP_FAIL;
373                  goto close;
374              }
375          } else {
376              uint32_t rval;
377              int cnt = fread(&rval, sizeof(rval), 1, f);
378              if (cnt != 1) {
379                  esp_rom_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
380                  args->result = ESP_FAIL;
381                  goto close;
382              }
383          }
384      }
385      args->result = ESP_OK;
386  
387  close:
388      fclose(f);
389  
390  done:
391      xSemaphoreGive(args->done);
392      vTaskDelay(1);
393      vTaskDelete(NULL);
394  }
395  
396  void test_spiffs_concurrent(const char* filename_prefix)
397  {
398      char names[4][64];
399      for (size_t i = 0; i < 4; ++i) {
400          snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
401          unlink(names[i]);
402      }
403  
404      read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
405      read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
406  
407      printf("writing f1 and f2\n");
408      const int cpuid_0 = 0;
409      const int cpuid_1 = portNUM_PROCESSORS - 1;
410      xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
411      xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
412  
413      xSemaphoreTake(args1.done, portMAX_DELAY);
414      printf("f1 done\n");
415      TEST_ASSERT_EQUAL(ESP_OK, args1.result);
416      xSemaphoreTake(args2.done, portMAX_DELAY);
417      printf("f2 done\n");
418      TEST_ASSERT_EQUAL(ESP_OK, args2.result);
419  
420      args1.write = false;
421      args2.write = false;
422      read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
423      read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
424  
425      printf("reading f1 and f2, writing f3 and f4\n");
426  
427      xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, cpuid_1);
428      xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, cpuid_0);
429      xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
430      xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
431  
432      xSemaphoreTake(args1.done, portMAX_DELAY);
433      printf("f1 done\n");
434      TEST_ASSERT_EQUAL(ESP_OK, args1.result);
435      xSemaphoreTake(args2.done, portMAX_DELAY);
436      printf("f2 done\n");
437      TEST_ASSERT_EQUAL(ESP_OK, args2.result);
438      xSemaphoreTake(args3.done, portMAX_DELAY);
439      printf("f3 done\n");
440      TEST_ASSERT_EQUAL(ESP_OK, args3.result);
441      xSemaphoreTake(args4.done, portMAX_DELAY);
442      printf("f4 done\n");
443      TEST_ASSERT_EQUAL(ESP_OK, args4.result);
444  
445      vSemaphoreDelete(args1.done);
446      vSemaphoreDelete(args2.done);
447      vSemaphoreDelete(args3.done);
448      vSemaphoreDelete(args4.done);
449  }
450  
451  
452  static void test_setup(void)
453  {
454      esp_vfs_spiffs_conf_t conf = {
455        .base_path = "/spiffs",
456        .partition_label = spiffs_test_partition_label,
457        .max_files = 5,
458        .format_if_mount_failed = true
459      };
460  
461      TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
462  }
463  
464  static void test_teardown(void)
465  {
466      TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
467  }
468  
469  TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]")
470  {
471      const esp_partition_t* part = get_test_data_partition();
472      TEST_ASSERT_NOT_NULL(part);
473      TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
474      test_setup();
475      size_t total = 0, used = 0;
476      TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used));
477      printf("total: %d, used: %d\n", total, used);
478      TEST_ASSERT_EQUAL(0, used);
479      test_teardown();
480  }
481  
482  TEST_CASE("can format mounted partition", "[spiffs]")
483  {
484      // Mount SPIFFS, create file, format, check that the file does not exist.
485      const esp_partition_t* part = get_test_data_partition();
486      TEST_ASSERT_NOT_NULL(part);
487      test_setup();
488      const char* filename = "/spiffs/hello.txt";
489      test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
490      esp_spiffs_format(part->label);
491      FILE* f = fopen(filename, "r");
492      TEST_ASSERT_NULL(f);
493      test_teardown();
494  }
495  
496  TEST_CASE("can format unmounted partition", "[spiffs]")
497  {
498      // Mount SPIFFS, create file, unmount. Format. Mount again, check that
499      // the file does not exist.
500      const esp_partition_t* part = get_test_data_partition();
501      TEST_ASSERT_NOT_NULL(part);
502      test_setup();
503      const char* filename = "/spiffs/hello.txt";
504      test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
505      test_teardown();
506      esp_spiffs_format(part->label);
507      // Don't use test_setup here, need to mount without formatting
508      esp_vfs_spiffs_conf_t conf = {
509          .base_path = "/spiffs",
510          .partition_label = spiffs_test_partition_label,
511          .max_files = 5,
512          .format_if_mount_failed = false
513      };
514      TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
515      FILE* f = fopen(filename, "r");
516      TEST_ASSERT_NULL(f);
517      test_teardown();
518  }
519  
520  TEST_CASE("can create and write file", "[spiffs]")
521  {
522      test_setup();
523      test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
524      test_teardown();
525  }
526  
527  TEST_CASE("can read file", "[spiffs]")
528  {
529      test_setup();
530      test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
531      test_spiffs_read_file("/spiffs/hello.txt");
532      test_teardown();
533  }
534  
535  TEST_CASE("can open maximum number of files", "[spiffs]")
536  {
537      size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
538      esp_vfs_spiffs_conf_t conf = {
539          .base_path = "/spiffs",
540          .partition_label = spiffs_test_partition_label,
541          .format_if_mount_failed = true,
542          .max_files = max_files
543      };
544      TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
545      test_spiffs_open_max_files("/spiffs/f", max_files);
546      TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
547  }
548  
549  TEST_CASE("overwrite and append file", "[spiffs]")
550  {
551      test_setup();
552      test_spiffs_overwrite_append("/spiffs/hello.txt");
553      test_teardown();
554  }
555  
556  TEST_CASE("can lseek", "[spiffs]")
557  {
558      test_setup();
559      test_spiffs_lseek("/spiffs/seek.txt");
560      test_teardown();
561  }
562  
563  
564  TEST_CASE("stat returns correct values", "[spiffs]")
565  {
566      test_setup();
567      test_spiffs_stat("/spiffs/stat.txt");
568      test_teardown();
569  }
570  
571  TEST_CASE("unlink removes a file", "[spiffs]")
572  {
573      test_setup();
574      test_spiffs_unlink("/spiffs/unlink.txt");
575      test_teardown();
576  }
577  
578  TEST_CASE("rename moves a file", "[spiffs]")
579  {
580      test_setup();
581      test_spiffs_rename("/spiffs/move");
582      test_teardown();
583  }
584  
585  TEST_CASE("can opendir root directory of FS", "[spiffs]")
586  {
587      test_setup();
588      test_spiffs_can_opendir("/spiffs");
589      test_teardown();
590  }
591  
592  TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]")
593  {
594      test_setup();
595      test_spiffs_opendir_readdir_rewinddir("/spiffs/dir");
596      test_teardown();
597  }
598  
599  TEST_CASE("readdir with large number of files", "[spiffs][timeout=30]")
600  {
601      test_setup();
602      test_spiffs_readdir_many_files("/spiffs/dir2");
603      test_teardown();
604  }
605  
606  TEST_CASE("multiple tasks can use same volume", "[spiffs]")
607  {
608      test_setup();
609      test_spiffs_concurrent("/spiffs/f");
610      test_teardown();
611  }
612  
613  #ifdef CONFIG_SPIFFS_USE_MTIME
614  TEST_CASE("mtime is updated when file is opened", "[spiffs]")
615  {
616      /* Open a file, check that mtime is set correctly */
617      const char* filename = "/spiffs/time";
618      test_setup();
619      time_t t_before_create = time(NULL);
620      test_spiffs_create_file_with_text(filename, "\n");
621      time_t t_after_create = time(NULL);
622  
623      struct stat st;
624      TEST_ASSERT_EQUAL(0, stat(filename, &st));
625      printf("mtime=%d\n", (int) st.st_mtime);
626      TEST_ASSERT(st.st_mtime >= t_before_create
627               && st.st_mtime <= t_after_create);
628  
629      /* Wait a bit, open again, check that mtime is updated */
630      vTaskDelay(2000 / portTICK_PERIOD_MS);
631      time_t t_before_open = time(NULL);
632      FILE *f = fopen(filename, "a");
633      time_t t_after_open = time(NULL);
634      TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
635      printf("mtime=%d\n", (int) st.st_mtime);
636      TEST_ASSERT(st.st_mtime >= t_before_open
637               && st.st_mtime <= t_after_open);
638      fclose(f);
639  
640      /* Wait a bit, open for reading, check that mtime is not updated */
641      vTaskDelay(2000 / portTICK_PERIOD_MS);
642      time_t t_before_open_ro = time(NULL);
643      f = fopen(filename, "r");
644      TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
645      printf("mtime=%d\n", (int) st.st_mtime);
646      TEST_ASSERT(t_before_open_ro > t_after_open
647               && st.st_mtime >= t_before_open
648               && st.st_mtime <= t_after_open);
649      fclose(f);
650  
651      test_teardown();
652  }
653  
654  TEST_CASE("utime() works well", "[spiffs]")
655  {
656      const char filename[] = "/spiffs/utime.txt";
657      struct stat achieved_stat;
658      struct tm desired_tm;
659      struct utimbuf desired_time = {
660          .actime = 0, // access time is not supported
661          .modtime = 0,
662      };
663      time_t false_now = 0;
664      memset(&desired_tm, 0, sizeof(struct tm));
665  
666      test_setup();
667      {
668          // Setting up a false actual time - used when the file is created and for modification with the current time
669          desired_tm.tm_mon = 10 - 1;
670          desired_tm.tm_mday = 31;
671          desired_tm.tm_year = 2018 - 1900;
672          desired_tm.tm_hour = 10;
673          desired_tm.tm_min = 35;
674          desired_tm.tm_sec = 23;
675  
676          false_now = mktime(&desired_tm);
677  
678          struct timeval now = { .tv_sec = false_now };
679          settimeofday(&now, NULL);
680      }
681      test_spiffs_create_file_with_text(filename, "");
682  
683      // 00:00:00. January 1st, 1900
684      desired_tm.tm_mon = 1 - 1;
685      desired_tm.tm_mday = 1;
686      desired_tm.tm_year = 0;
687      desired_tm.tm_hour = 0;
688      desired_tm.tm_min = 0;
689      desired_tm.tm_sec = 0;
690      printf("Testing mod. time: %s", asctime(&desired_tm));
691      desired_time.modtime = mktime(&desired_tm);
692      TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
693      TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
694      TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
695  
696      // 23:59:08. December 31st, 2145
697      desired_tm.tm_mon = 12 - 1;
698      desired_tm.tm_mday = 31;
699      desired_tm.tm_year = 2145 - 1900;
700      desired_tm.tm_hour = 23;
701      desired_tm.tm_min = 59;
702      desired_tm.tm_sec = 8;
703      printf("Testing mod. time: %s", asctime(&desired_tm));
704      desired_time.modtime = mktime(&desired_tm);
705      TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
706      TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
707      TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
708  
709      // Current time
710      TEST_ASSERT_EQUAL(0, utime(filename, NULL));
711      TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
712      printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
713      TEST_ASSERT_NOT_EQUAL(desired_time.modtime, achieved_stat.st_mtime);
714      TEST_ASSERT(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
715  
716      test_teardown();
717  }
718  #endif // CONFIG_SPIFFS_USE_MTIME