-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(storage): add a test app for std::filesystem features
- Loading branch information
Showing
15 changed files
with
610 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# The following five lines of boilerplate have to be in your project's | ||
# CMakeLists in this exact order for cmake to work correctly | ||
cmake_minimum_required(VERSION 3.16) | ||
|
||
include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||
set(COMPONENTS main) | ||
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults") | ||
project(std_filesystem_test) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
| Supported Targets | ESP32 | ESP32-C3 | | ||
| ----------------- | ----- | -------- | | ||
|
||
This is a test app which verifies that std::filesystem features work in ESP-IDF. The tests are written using [Catch2](https://github.com/catchorg/Catch2) managed [component](https://components.espressif.com/components/espressif/catch2/). | ||
|
||
To run the tests: | ||
|
||
```shell | ||
idf.py flash monitor | ||
``` | ||
|
||
Or, in QEMU: | ||
|
||
```shell | ||
idf.py qemu monitor | ||
``` | ||
|
||
Or, using pytest: | ||
|
||
```shell | ||
idf.py build | ||
pytest --embedded-services idf,qemu --target esp32 --ignore managed_components | ||
``` | ||
|
||
## Feature Support | ||
|
||
Please update `_cplusplus_filesystem` section in cplusplus.rst when modifying this table. | ||
|
||
| Feature | Supported | Tested | Comment | | ||
|------------------------------|-----------|--------|---------------------------------------------------------------------------------------------------------------| | ||
| absolute | y | y | | | ||
| canonical | y | y | | | ||
| weakly_canonical | y | y | | | ||
| relative | y | y | | | ||
| proximate | y | y | | | ||
| copy | y | y | this function has complex behavior, not sure about test coverage | | ||
| copy_file | y | y | | | ||
| copy_symlink | n | n | symlinks are not supported | | ||
| create_directory | y | y | | | ||
| create_directories | y | y | | | ||
| create_hard_link | n | n | hard links are not supported | | ||
| create_symlink | n | n | symlinks are not supported | | ||
| create_directory_symlink | n | n | symlinks are not supported | | ||
| current_path | partial | y | setting path is not supported in IDF | | ||
| exists | y | y | | | ||
| equivalent | y | y | | | ||
| file_size | y | y | | | ||
| hard_link_count | n | n | hard links are not supported | | ||
| last_write_time | y | y | | | ||
| permissions | partial | y | setting permissions is not supported | | ||
| read_symlink | n | n | symlinks are not supported | | ||
| remove | y | y | | | ||
| remove_all | y | y | | | ||
| rename | y | y | | | ||
| resize_file | n | y | doesn't work, toolchain has to be built with _GLIBCXX_HAVE_TRUNCATE | | ||
| space | n | y | doesn't work, toolchain has to be built with _GLIBCXX_HAVE_SYS_STATVFS_H and statvfs function must be defined | | ||
| status | y | y | | | ||
| symlink_status | n | n | symlinks are not supported | | ||
| temp_directory_path | y | y | works if /tmp directory has been mounted | | ||
| directory_iterator | y | y | | | ||
| recursive_directory_iterator | y | y | | | ||
| is_block_file | y | y | | | ||
| is_character_file | y | y | | | ||
| is_directory | y | y | | | ||
| is_empty | y | y | | | ||
| is_fifo | y | y | | | ||
| is_other | n | n | | | ||
| is_regular_file | y | y | | | ||
| is_socket | y | y | | | ||
| is_symlink | y | y | | |
10 changes: 10 additions & 0 deletions
10
tools/test_apps/storage/std_filesystem/main/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
idf_component_register(SRCS | ||
"test_std_filesystem_main.cpp" | ||
"test_ops.cpp" | ||
"test_paths.cpp" | ||
"test_status.cpp" | ||
INCLUDE_DIRS "." | ||
PRIV_REQUIRES vfs fatfs | ||
WHOLE_ARCHIVE) | ||
|
||
fatfs_create_spiflash_image(storage ${CMAKE_CURRENT_LIST_DIR}/test_fs_image FLASH_IN_PROJECT) |
2 changes: 2 additions & 0 deletions
2
tools/test_apps/storage/std_filesystem/main/idf_component.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dependencies: | ||
espressif/catch2: "^3.7.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1234567890 |
Empty file.
Empty file.
196 changes: 196 additions & 0 deletions
196
tools/test_apps/storage/std_filesystem/main/test_ops.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <cstddef> | ||
#include <filesystem> | ||
#include <catch2/catch_test_macros.hpp> | ||
#include <stdio.h> | ||
#include <sys/stat.h> | ||
#include <sys/utime.h> | ||
#include "esp_vfs_fat.h" | ||
#include "wear_levelling.h" | ||
|
||
class OpsTest { | ||
private: | ||
wl_handle_t m_wl_handle; | ||
public: | ||
OpsTest() | ||
{ | ||
esp_vfs_fat_mount_config_t mount_config = VFS_FAT_MOUNT_DEFAULT_CONFIG(); | ||
|
||
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl("/test", "storage", &mount_config, &m_wl_handle); | ||
if (err != ESP_OK) { | ||
throw std::runtime_error("Failed to mount FAT filesystem"); | ||
} | ||
} | ||
~OpsTest() | ||
{ | ||
esp_vfs_fat_spiflash_unmount_rw_wl("/test", m_wl_handle); | ||
} | ||
|
||
void test_create_remove() | ||
{ | ||
std::filesystem::create_directory("/test/dir"); | ||
CHECK(std::filesystem::exists("/test/dir")); | ||
|
||
CHECK(std::filesystem::remove("/test/dir")); | ||
CHECK(!std::filesystem::exists("/test/dir")); | ||
} | ||
|
||
/* | ||
The following two tests rely on the following directory structure | ||
in the generated FAT filesystem: | ||
/test | ||
└── test_dir_iter | ||
├── dir1 | ||
│ └── f1 | ||
└── dir2 | ||
└── dir3 | ||
└── f3 | ||
*/ | ||
void test_directory_iterator() | ||
{ | ||
std::filesystem::directory_iterator it("/test/test_dir_iter"); | ||
CHECK(it != std::filesystem::directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir1"); | ||
CHECK(it->is_directory()); | ||
++it; | ||
CHECK(it != std::filesystem::directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir2"); | ||
CHECK(it->is_directory()); | ||
++it; | ||
CHECK(it == std::filesystem::directory_iterator()); | ||
} | ||
|
||
void test_recursive_directory_iterator() | ||
{ | ||
std::filesystem::recursive_directory_iterator it("/test/test_dir_iter"); | ||
CHECK(it != std::filesystem::recursive_directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir1"); | ||
CHECK(it->is_directory()); | ||
++it; | ||
CHECK(it != std::filesystem::recursive_directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir1/f1"); | ||
CHECK(it->is_regular_file()); | ||
++it; | ||
CHECK(it != std::filesystem::recursive_directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir2"); | ||
CHECK(it->is_directory()); | ||
++it; | ||
CHECK(it != std::filesystem::recursive_directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir2/dir3"); | ||
CHECK(it->is_directory()); | ||
++it; | ||
CHECK(it != std::filesystem::recursive_directory_iterator()); | ||
CHECK(it->path() == "/test/test_dir_iter/dir2/dir3/f3"); | ||
CHECK(it->is_regular_file()); | ||
++it; | ||
CHECK(it == std::filesystem::recursive_directory_iterator()); | ||
} | ||
|
||
void test_copy_remove_recursive_copy() | ||
{ | ||
if (std::filesystem::exists("/test/copy_dir")) { | ||
CHECK(std::filesystem::remove_all("/test/copy_dir")); | ||
} | ||
|
||
CHECK(std::filesystem::create_directory("/test/copy_dir")); | ||
REQUIRE_NOTHROW(std::filesystem::copy("/test/test_dir_iter/dir1/f1", "/test/copy_dir/f1")); | ||
CHECK(std::filesystem::exists("/test/copy_dir/f1")); | ||
CHECK(std::filesystem::remove("/test/copy_dir/f1")); | ||
CHECK(std::filesystem::remove("/test/copy_dir")); | ||
|
||
REQUIRE_NOTHROW(std::filesystem::copy("/test/test_dir_iter", "/test/copy_dir", std::filesystem::copy_options::recursive)); | ||
CHECK(std::filesystem::exists("/test/copy_dir/dir1/f1")); | ||
CHECK(std::filesystem::exists("/test/copy_dir/dir2/dir3/f3")); | ||
CHECK(std::filesystem::remove_all("/test/copy_dir")); | ||
} | ||
|
||
void test_create_directories() | ||
{ | ||
if (std::filesystem::exists("/test/create_dir")) { | ||
CHECK(std::filesystem::remove_all("/test/create_dir")); | ||
} | ||
CHECK(std::filesystem::create_directories("/test/create_dir/dir1/dir2")); | ||
CHECK(std::filesystem::exists("/test/create_dir/dir1/dir2")); | ||
CHECK(std::filesystem::remove_all("/test/create_dir")); | ||
} | ||
|
||
void test_rename_file() | ||
{ | ||
if (std::filesystem::exists("/test/rename_file")) { | ||
CHECK(std::filesystem::remove("/test/rename_file")); | ||
} | ||
std::filesystem::create_directory("/test/rename_file"); | ||
std::filesystem::copy_file("/test/file", "/test/rename_file/file"); | ||
CHECK(std::filesystem::exists("/test/rename_file/file")); | ||
std::filesystem::rename("/test/rename_file/file", "/test/rename_file/file2"); | ||
CHECK(std::filesystem::exists("/test/rename_file/file2")); | ||
CHECK(std::filesystem::remove_all("/test/rename_file")); | ||
} | ||
|
||
void test_file_size_resize() | ||
{ | ||
if (std::filesystem::exists("/test/file_size")) { | ||
CHECK(std::filesystem::remove("/test/file_size")); | ||
} | ||
std::filesystem::copy_file("/test/file", "/test/file_size"); | ||
CHECK(std::filesystem::file_size("/test/file_size") == 11); | ||
// Not supported: libstdc++ has to be built with _GLIBCXX_HAVE_TRUNCATE | ||
CHECK_THROWS(std::filesystem::resize_file("/test/file_size", 20)); | ||
CHECK(std::filesystem::remove("/test/file_size")); | ||
} | ||
|
||
void test_file_last_write_time() | ||
{ | ||
if (std::filesystem::exists("/test/file_time")) { | ||
CHECK(std::filesystem::remove("/test/file_time")); | ||
} | ||
std::filesystem::copy_file("/test/file", "/test/file_time"); | ||
auto time = std::filesystem::last_write_time("/test/file_time"); | ||
struct stat st = {}; | ||
stat("/test/file_time", &st); | ||
struct utimbuf times = {st.st_atime, st.st_mtime + 1000000000}; | ||
utime("/test/file_time", ×); | ||
|
||
auto time2 = std::filesystem::last_write_time("/test/file_time"); | ||
CHECK(time2 > time); | ||
} | ||
|
||
void test_space() | ||
{ | ||
// Not supported: libstdc++ has to be built with _GLIBCXX_HAVE_SYS_STATVFS_H and statvfs function | ||
// has to be defined | ||
CHECK_THROWS(std::filesystem::space("/test")); | ||
} | ||
|
||
void test_permissions() | ||
{ | ||
auto perm = std::filesystem::status("/test/file").permissions(); | ||
CHECK(perm == std::filesystem::perms::all); | ||
|
||
std::filesystem::permissions("/test/file", std::filesystem::perms::owner_read, std::filesystem::perm_options::replace); | ||
|
||
// setting permissions is not supported and has no effect | ||
perm = std::filesystem::status("/test/file").permissions(); | ||
CHECK(perm == std::filesystem::perms::all); | ||
} | ||
|
||
|
||
// when adding a test method, don't forget to add it to the list below | ||
}; | ||
|
||
METHOD_AS_TEST_CASE(OpsTest::test_create_remove, "Test create and remove directories"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_directory_iterator, "Test directory iterator"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_recursive_directory_iterator, "Test recursive directory iterator"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_copy_remove_recursive_copy, "Test copy, remove and recursive copy"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_create_directories, "Test create directories"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_rename_file, "Test rename file"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_file_size_resize, "Test file size and resize"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_file_last_write_time, "Test file last write time"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_space, "Test space"); | ||
METHOD_AS_TEST_CASE(OpsTest::test_permissions, "Test permissions"); |
44 changes: 44 additions & 0 deletions
44
tools/test_apps/storage/std_filesystem/main/test_paths.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <filesystem> | ||
#include <catch2/catch_test_macros.hpp> | ||
|
||
TEST_CASE("std::filesystem path, relative, proximate, absolute") | ||
{ | ||
// In IDF, CWD is always in the the root directory | ||
CHECK(std::filesystem::current_path() == "/"); | ||
|
||
// Create absolute path from relative path | ||
std::filesystem::path rel_path("test/file.txt"); | ||
std::filesystem::path abs_path = std::filesystem::absolute(rel_path); | ||
CHECK(abs_path == "/test/file.txt"); | ||
|
||
// Create relative path from absolute path | ||
std::filesystem::path rel_path2 = std::filesystem::relative(abs_path); | ||
CHECK(rel_path2 == "test/file.txt"); | ||
|
||
// Create relative path from absolute path with different base | ||
std::filesystem::path rel_path3 = std::filesystem::relative(abs_path, "/test"); | ||
CHECK(rel_path3 == "file.txt"); | ||
|
||
std::filesystem::path prox_path = std::filesystem::proximate("/root1/file", "/root2"); | ||
CHECK(prox_path == "../root1/file"); | ||
} | ||
|
||
TEST_CASE("std::filesystem weakly_canonical") | ||
{ | ||
CHECK(std::filesystem::weakly_canonical("/a/b/c/./d/../e/f/../g") == "/a/b/c/e/g"); | ||
} | ||
|
||
TEST_CASE("std::filesystem current_path") | ||
{ | ||
// In IDF, CWD is always in the the root directory | ||
CHECK(std::filesystem::current_path() == "/"); | ||
|
||
// Changing the current path in IDF is not supported | ||
CHECK_THROWS(std::filesystem::current_path("/test")); | ||
} |
Oops, something went wrong.