diff --git a/src/fsfw_hal/CMakeLists.txt b/src/fsfw_hal/CMakeLists.txt index 057ab3a6..24d712aa 100644 --- a/src/fsfw_hal/CMakeLists.txt +++ b/src/fsfw_hal/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(devicehandlers) add_subdirectory(common) +add_subdirectory(host) if(UNIX) add_subdirectory(linux) diff --git a/src/fsfw_hal/host/CMakeLists.txt b/src/fsfw_hal/host/CMakeLists.txt index 8b137891..95b79675 100644 --- a/src/fsfw_hal/host/CMakeLists.txt +++ b/src/fsfw_hal/host/CMakeLists.txt @@ -1 +1 @@ - +target_sources(${LIB_FSFW_NAME} PUBLIC HostFilesystem.cpp) diff --git a/src/fsfw_hal/host/HostFilesystem.cpp b/src/fsfw_hal/host/HostFilesystem.cpp new file mode 100644 index 00000000..879b9377 --- /dev/null +++ b/src/fsfw_hal/host/HostFilesystem.cpp @@ -0,0 +1,99 @@ +#include "HostFilesystem.h" + +#include +#include + +#include "fsfw/serialize.h" + +using namespace std::filesystem; +using namespace std; + +HostFilesystem::HostFilesystem() = default; + +ReturnValue_t HostFilesystem::writeToFile(FileOpParams params, const uint8_t *data) { + if (params.path == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + path path(params.path); + if (not exists(path)) { + return HasFileSystemIF::FILE_DOES_NOT_EXIST; + } + ofstream file(path, ios::binary | ios::out); + if (file.fail()) { + return HasFileSystemIF::GENERIC_FILE_ERROR; + } + file.seekp(static_cast(params.offset)); + file.write(reinterpret_cast(data), static_cast(params.size)); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t HostFilesystem::readFromFile(FileOpParams params, uint8_t **buffer, size_t &readSize, + size_t maxSize) { + if (params.path == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + path path(params.path); + if (not exists(path)) { + return HasFileSystemIF::FILE_DOES_NOT_EXIST; + } + ifstream file(path); + if (file.fail()) { + return HasFileSystemIF::GENERIC_FILE_ERROR; + } + auto readLen = static_cast(params.offset); + file.seekg(readLen); + if (readSize + params.size > maxSize) { + return SerializeIF::BUFFER_TOO_SHORT; + } + file.read(reinterpret_cast(*buffer), readLen); + readSize += readLen; + *buffer += readLen; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t HostFilesystem::createFile(FilesystemParams params, const uint8_t *data, + size_t size) { + if (params.path == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + path path(params.path); + if (exists(path)) { + return HasFileSystemIF::FILE_ALREADY_EXISTS; + } + ofstream file(path); + if (file.fail()) { + return HasFileSystemIF::GENERIC_FILE_ERROR; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t HostFilesystem::removeFile(const char *path_, FileSystemArgsIF *args) { + if (path_ == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + path path(path_); + if (not exists(path)) { + return HasFileSystemIF::FILE_DOES_NOT_EXIST; + } + if (remove(path, errorCode)) { + return HasReturnvaluesIF::RETURN_OK; + } + return HasFileSystemIF::GENERIC_FILE_ERROR; +} +ReturnValue_t HostFilesystem::createDirectory(FilesystemParams params, bool createParentDirs) { + if (params.path == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + path dirPath(params.path); + if (createParentDirs) { + create_directories(dirPath, errorCode); + } + return 0; +} +ReturnValue_t HostFilesystem::removeDirectory(FilesystemParams params, bool deleteRecurively) { + return 0; +} +ReturnValue_t HostFilesystem::renameFile(const char *oldPath, char *newPath, + FileSystemArgsIF *args) { + return 0; +} diff --git a/src/fsfw_hal/host/HostFilesystem.h b/src/fsfw_hal/host/HostFilesystem.h new file mode 100644 index 00000000..2dd8f2f5 --- /dev/null +++ b/src/fsfw_hal/host/HostFilesystem.h @@ -0,0 +1,24 @@ +#ifndef FSFW_HAL_HOSTFILESYSTEM_H +#define FSFW_HAL_HOSTFILESYSTEM_H + +#include + +#include "fsfw/filesystem/HasFileSystemIF.h" + +class HostFilesystem : public HasFileSystemIF { + public: + HostFilesystem(); + ReturnValue_t writeToFile(FileOpParams params, const uint8_t *data) override; + ReturnValue_t readFromFile(FileOpParams fileOpInfo, uint8_t **buffer, size_t &readSize, + size_t maxSize) override; + ReturnValue_t createFile(FilesystemParams params, const uint8_t *data, size_t size) override; + ReturnValue_t removeFile(const char *path, FileSystemArgsIF *args) override; + ReturnValue_t createDirectory(FilesystemParams params, bool createParentDirs) override; + ReturnValue_t removeDirectory(FilesystemParams params, bool deleteRecurively) override; + ReturnValue_t renameFile(const char *oldPath, char *newPath, FileSystemArgsIF *args) override; + + std::error_code errorCode; + + private: +}; +#endif // FSFW_HAL_HOSTFILESYSTEM_H diff --git a/unittests/hal/CMakeLists.txt b/unittests/hal/CMakeLists.txt index 152bb39f..5e2887b2 100644 --- a/unittests/hal/CMakeLists.txt +++ b/unittests/hal/CMakeLists.txt @@ -1 +1,2 @@ -target_sources(${FSFW_TEST_TGT} PRIVATE testCommandExecutor.cpp) +target_sources(${FSFW_TEST_TGT} PRIVATE testCommandExecutor.cpp + testHostFilesystem.cpp) diff --git a/unittests/hal/testCommandExecutor.cpp b/unittests/hal/testCommandExecutor.cpp index d34f67aa..6c2bb1fe 100644 --- a/unittests/hal/testCommandExecutor.cpp +++ b/unittests/hal/testCommandExecutor.cpp @@ -15,7 +15,7 @@ static const char TEST_FILE_NAME[] = "/tmp/fsfw-unittest-test.txt"; -TEST_CASE("Command Executor", "[cmd-exec]") { +TEST_CASE("Command Executor", "[hal][linux]") { // Check blocking mode first CommandExecutor cmdExecutor(1024); std::string cmd = "echo \"test\" >> " + std::string(TEST_FILE_NAME); diff --git a/unittests/hal/testHostFilesystem.cpp b/unittests/hal/testHostFilesystem.cpp new file mode 100644 index 00000000..19af1c6b --- /dev/null +++ b/unittests/hal/testHostFilesystem.cpp @@ -0,0 +1,5 @@ +#include + +#include "fsfw_hal/host/HostFilesystem.h" + +TEST_CASE("Host Filesystem", "[hal][host]") { auto hostFs = HostFilesystem(); } \ No newline at end of file diff --git a/unittests/mocks/FilesystemMock.cpp b/unittests/mocks/FilesystemMock.cpp index 5cf833c0..e4efd086 100644 --- a/unittests/mocks/FilesystemMock.cpp +++ b/unittests/mocks/FilesystemMock.cpp @@ -1,6 +1,76 @@ #include "FilesystemMock.h" +#include "fsfw/serialize/SerializeIF.h" + ReturnValue_t FilesystemMock::writeToFile(FileOpParams params, const uint8_t *data) { + createOrAddToFile(params, data); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FilesystemMock::readFromFile(FileOpParams params, uint8_t **buffer, size_t &readSize, + size_t maxSize) { + std::string filename(params.path); + auto iter = fileMap.find(filename); + if (iter == fileMap.end()) { + return HasFileSystemIF::FILE_DOES_NOT_EXIST; + } else { + FileInfo &info = iter->second; + size_t readLen = params.size; + if (params.offset + params.size > info.fileRaw.size()) { + if (params.offset > info.fileRaw.size()) { + return HasReturnvaluesIF::RETURN_OK; + } + readLen = info.fileRaw.size() - params.offset; + } + if (readSize + readLen > maxSize) { + return SerializeIF::STREAM_TOO_SHORT; + } + std::copy(info.fileRaw.data() + params.offset, info.fileRaw.data() + readLen, *buffer); + *buffer += readLen; + readSize += readLen; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FilesystemMock::createFile(FilesystemParams params, const uint8_t *data, + size_t size) { + FileOpParams params2(params.path, size); + createOrAddToFile(params2, data); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FilesystemMock::removeFile(const char *path, FileSystemArgsIF *args) { + std::string filename(path); + auto iter = fileMap.find(filename); + if (iter == fileMap.end()) { + return HasFileSystemIF::FILE_DOES_NOT_EXIST; + } else { + fileMap.erase(iter); + return HasReturnvaluesIF::RETURN_OK; + } +} + +ReturnValue_t FilesystemMock::createDirectory(FilesystemParams params, bool createParentDirs) { + std::string dirPath = params.path; + dirMap[dirPath].createCallCount++; + dirMap[dirPath].wihParentDir.push(createParentDirs); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FilesystemMock::removeDirectory(FilesystemParams params, bool deleteRecurively) { + std::string dirPath = params.path; + dirMap[dirPath].delCallCount++; + dirMap[dirPath].recursiveDeletion.push(deleteRecurively); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FilesystemMock::renameFile(const char *oldPath, char *newPath, + FileSystemArgsIF *args) { + renameQueue.push(RenameInfo(oldPath, newPath)); + return HasReturnvaluesIF::RETURN_OK; +} + +void FilesystemMock::createOrAddToFile(FileOpParams params, const uint8_t *data) { std::string filename(params.path); auto iter = fileMap.find(filename); if (iter == fileMap.end()) { @@ -8,40 +78,31 @@ ReturnValue_t FilesystemMock::writeToFile(FileOpParams params, const uint8_t *da queue.push(FileWriteInfo(filename, params.offset, data, params.size)); FileInfo info; info.fileSegQueue = queue; - info.fileRaw.insert(info.fileRaw.end(), data, data + params.size); + if (data != nullptr) { + info.fileRaw.insert(info.fileRaw.end(), data, data + params.size); + } fileMap.emplace(filename, info); } else { - FileInfo& info = iter->second; + FileInfo &info = iter->second; info.fileSegQueue.push(FileWriteInfo(filename, params.offset, data, params.size)); + if (data == nullptr) { + return; + } // Easiest case: append data to the end - if(params.offset == info.fileRaw.size()) { + if (params.offset == info.fileRaw.size()) { info.fileRaw.insert(info.fileRaw.end(), data, data + params.size); } else { size_t totalNewLen = params.offset + params.size; - if(totalNewLen > info.fileRaw.size()) { + if (totalNewLen > info.fileRaw.size()) { info.fileRaw.resize(params.offset + params.size); } - std::copy(data, data + params.size, info.fileRaw.begin() + static_cast(params.offset)); + std::copy(data, data + params.size, + info.fileRaw.begin() + static_cast(params.offset)); } } - return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t FilesystemMock::readFromFile(FileOpParams fileOpInfo, uint8_t **buffer, - size_t &readSize, size_t maxSize) { - return 0; -} -ReturnValue_t FilesystemMock::createFile(FilesystemParams params, const uint8_t *data, - size_t size) { - return 0; -} -ReturnValue_t FilesystemMock::removeFile(const char *path, FileSystemArgsIF *args) { return 0; } -ReturnValue_t FilesystemMock::createDirectory(FilesystemParams params, bool createParentDirs) { - return 0; -} -ReturnValue_t FilesystemMock::removeDirectory(FilesystemParams params, bool deleteRecurively) { - return 0; -} -ReturnValue_t FilesystemMock::renameFile(const char *oldPath, char *newPath, - FileSystemArgsIF *args) { - return 0; + +void FilesystemMock::reset() { + fileMap.clear(); + dirMap.clear(); } diff --git a/unittests/mocks/FilesystemMock.h b/unittests/mocks/FilesystemMock.h index fbd157b0..b02928c7 100644 --- a/unittests/mocks/FilesystemMock.h +++ b/unittests/mocks/FilesystemMock.h @@ -1,18 +1,26 @@ #ifndef FSFW_MOCKS_FILESYSTEMMOCK_H #define FSFW_MOCKS_FILESYSTEMMOCK_H +#include #include #include -#include #include #include "fsfw/filesystem.h" +/** + * This mock models a filesystem in the RAM. It can be used to verify correct behaviour of + * a component using a filesystem without relying on an actual OS filesystem implementation. + * + * Please note that this object does not actually check paths for validity. The file API was + * built in a way to allow reading a file back after it was written while also remembering + * the specific file segments which were inserted in write calls. + */ class FilesystemMock : public HasFileSystemIF { public: struct FileWriteInfo { - FileWriteInfo(std::string filename, size_t offset, const uint8_t* data, size_t len) - : filename(std::move(filename)), offset(offset) { + FileWriteInfo(std::string filename, size_t offset, const uint8_t *data, size_t len) + : filename(std::move(filename)), offset(offset) { this->data.insert(this->data.end(), data, data + len); } std::string filename; @@ -28,14 +36,36 @@ class FilesystemMock : public HasFileSystemIF { std::map fileMap; + struct DirInfo { + size_t createCallCount = 0; + size_t delCallCount = 0; + std::queue wihParentDir; + std::queue recursiveDeletion; + }; + std::map dirMap; + + struct RenameInfo { + RenameInfo(std::string oldName, std::string newName) + : oldName(std::move(oldName)), newName(std::move(newName)) {} + + std::string oldName; + std::string newName; + }; + std::queue renameQueue; + ReturnValue_t writeToFile(FileOpParams params, const uint8_t *data) override; - ReturnValue_t readFromFile(FileOpParams fileOpInfo, uint8_t **buffer, size_t &readSize, + ReturnValue_t readFromFile(FileOpParams params, uint8_t **buffer, size_t &readSize, size_t maxSize) override; ReturnValue_t createFile(FilesystemParams params, const uint8_t *data, size_t size) override; ReturnValue_t removeFile(const char *path, FileSystemArgsIF *args) override; ReturnValue_t createDirectory(FilesystemParams params, bool createParentDirs) override; ReturnValue_t removeDirectory(FilesystemParams params, bool deleteRecurively) override; ReturnValue_t renameFile(const char *oldPath, char *newPath, FileSystemArgsIF *args) override; + + void reset(); + + private: + void createOrAddToFile(FileOpParams params, const uint8_t *data); }; #endif // FSFW_MOCKS_FILESYSTEMMOCK_H