Tactility/Modules/service-module/tests/source/service_paths_test.cpp
Ken Van Hoeylandt 79d3dd4fdb
Improvements for apps, filesystems, terminal and shell (#659)
app-module

- Apps now have support for:
  - environment variables (inherit from parent app)
  - current workdir
  - window size (when running in terminal)
- Add type aliases like `AppId` and `PackageId`
- Remove data duplication between ledger and app instance records: create `AppManifestSet`
- `AppManifest` usage safety improved: pass by value where needed, or use `AppId` instead of a manifest.

Terminal and shell

- Moved shell commands into coreutils-module as proper apps.
- Refine stack size for terminal and shell because apps now run with their own task and stack (including in-memory apps)

Platform

- Fix for listing root dir on ESP32: wrap posix directory functions and wire in file systems.
- Moved all platform-specific file system init code to platform subprojects

Other

- Fix for screenshot capture (issue with paths)
- Fixes for coreutils app correctness
- Memory optimizations (use more PSRAM where possible) in various locations such as app-module, shell and terminal.
- `ls` and `help` commands now use terminal width and show columns of data by default
- Created fs helpers at `TactilityKernel/include/tactility/filesystem/fs.h` (partially ported from `ShellFs`)
2026-09-25 08:29:27 +02:00

107 lines
4.3 KiB
C++

#include "doctest.h"
#include <service/paths.h>
#include <tactility/filesystem/file_system.h>
#include <tactility/paths.h>
#include <cstring>
#include <string>
namespace {
// paths_get_data_path() resolves its root via file_system_find_by_name("data") (see
// TactilityKernel/source/paths.cpp), registered in real builds by platform-esp32/platform-posix's
// own module start(). This test binary has no reason to run from a directory with a real "data"
// folder, so a fake one is registered here instead, for every test case in this file.
error_t fake_mount(void*) { return ERROR_NONE; }
error_t fake_unmount(void*) { return ERROR_NONE; }
// Never actually mount()ed below (this fake lives for the whole process), so
// file_system_remove()'s "must be unmounted first" precondition never applies to it anyway.
bool fake_is_mounted(void*) { return false; }
error_t fake_get_path(void* data, char* out_path, size_t out_path_size) {
const char* path = static_cast<const char*>(data);
if (std::strlen(path) + 1 > out_path_size) {
return ERROR_BUFFER_OVERFLOW;
}
std::strcpy(out_path, path);
return ERROR_NONE;
}
const FileSystemApi fake_data_api = {
.mount = fake_mount,
.unmount = fake_unmount,
.is_mounted = fake_is_mounted,
.get_path = fake_get_path,
};
char fake_data_path[] = "data";
FileSystem* fake_data_fs = file_system_add("data", &fake_data_api, fake_data_path);
} // namespace
TEST_CASE("paths_get_data_path returns a non-empty path") {
char buffer[FILE_MAX_PATH_STRING_LENGTH];
REQUIRE_EQ(paths_get_data_path(buffer, sizeof(buffer)), ERROR_NONE);
CHECK_GT(std::strlen(buffer), 0);
}
TEST_CASE("paths_get_data_path reports overflow for a too-small buffer") {
char buffer[1];
CHECK_EQ(paths_get_data_path(buffer, sizeof(buffer)), ERROR_BUFFER_OVERFLOW);
}
TEST_CASE("service_paths_get_user_data_directory includes the service id") {
char root[FILE_MAX_PATH_STRING_LENGTH];
REQUIRE_EQ(paths_get_data_path(root, sizeof(root)), ERROR_NONE);
char buffer[FILE_MAX_PATH_STRING_LENGTH];
CHECK_EQ(service_paths_get_user_data_directory("my-service", buffer, sizeof(buffer)), ERROR_NONE);
std::string expected = std::string(root) + "/service/my-service";
CHECK_EQ(std::string(buffer), expected);
}
TEST_CASE("service_paths_get_user_data_path appends the child path") {
char directory[FILE_MAX_PATH_STRING_LENGTH];
REQUIRE_EQ(service_paths_get_user_data_directory("my-service", directory, sizeof(directory)), ERROR_NONE);
char buffer[256];
CHECK_EQ(service_paths_get_user_data_path("my-service", "settings.properties", buffer, sizeof(buffer)), ERROR_NONE);
std::string expected = std::string(directory) + "/settings.properties";
CHECK_EQ(std::string(buffer), expected);
}
TEST_CASE("service_paths_get_assets_directory is nested under the user data directory") {
char directory[FILE_MAX_PATH_STRING_LENGTH];
REQUIRE_EQ(service_paths_get_user_data_directory("my-service", directory, sizeof(directory)), ERROR_NONE);
char buffer[FILE_MAX_PATH_STRING_LENGTH];
CHECK_EQ(service_paths_get_assets_directory("my-service", buffer, sizeof(buffer)), ERROR_NONE);
std::string expected = std::string(directory) + "/assets";
CHECK_EQ(std::string(buffer), expected);
}
TEST_CASE("service_paths_get_assets_path appends the child path") {
char directory[FILE_MAX_PATH_STRING_LENGTH];
REQUIRE_EQ(service_paths_get_assets_directory("my-service", directory, sizeof(directory)), ERROR_NONE);
char buffer[FILE_MAX_PATH_STRING_LENGTH];
CHECK_EQ(service_paths_get_assets_path("my-service", "icon.png", buffer, sizeof(buffer)), ERROR_NONE);
std::string expected = std::string(directory) + "/icon.png";
CHECK_EQ(std::string(buffer), expected);
}
TEST_CASE("service_paths functions report overflow for a too-small buffer") {
char buffer[1];
CHECK_EQ(service_paths_get_user_data_directory("my-service", buffer, sizeof(buffer)), ERROR_BUFFER_OVERFLOW);
CHECK_EQ(service_paths_get_user_data_path("my-service", "child", buffer, sizeof(buffer)), ERROR_BUFFER_OVERFLOW);
CHECK_EQ(service_paths_get_assets_directory("my-service", buffer, sizeof(buffer)), ERROR_BUFFER_OVERFLOW);
CHECK_EQ(service_paths_get_assets_path("my-service", "child", buffer, sizeof(buffer)), ERROR_BUFFER_OVERFLOW);
}