diff --git a/Fw/Cfg/ConfigCheck.cpp b/Fw/Cfg/ConfigCheck.cpp index da218bcccf..e4ed04c6af 100644 --- a/Fw/Cfg/ConfigCheck.cpp +++ b/Fw/Cfg/ConfigCheck.cpp @@ -11,6 +11,7 @@ */ #include +#include // Check that command/telemetry strings are not larger than an argument buffer @@ -23,3 +24,10 @@ static_assert(FW_PARAM_STRING_MAX_SIZE <= FW_PARAM_BUFFER_MAX_SIZE, "FW_PARAM_ST // value. static_assert((FW_ENABLE_TEXT_LOGGING == 0) || ( FW_SERIALIZABLE_TO_STRING == 1), "FW_SERIALIZABLE_TO_STRING must be enabled to enable FW_ENABLE_TEXT_LOGGING"); +static_assert(std::numeric_limits::max() == std::numeric_limits::max() && + std::numeric_limits::min() == std::numeric_limits::min(), + "FwBuffSizeType must be equivalent to FwExternalSizeType"); + +static_assert(std::numeric_limits::max() >= std::numeric_limits::max() && + std::numeric_limits::min() <= std::numeric_limits::min(), + "FwSizeType cannot entirely store values of type FwExternalSizeType"); diff --git a/Fw/Dp/DpContainer.cpp b/Fw/Dp/DpContainer.cpp index d493346edf..a95551c29d 100644 --- a/Fw/Dp/DpContainer.cpp +++ b/Fw/Dp/DpContainer.cpp @@ -70,10 +70,9 @@ Fw::SerializeStatus DpContainer::deserializeHeader() { } // Deserialize the user data if (status == Fw::FW_SERIALIZE_OK) { - const bool omitLength = true; const FwSizeType requestedSize = sizeof this->m_userData; FwSizeType receivedSize = requestedSize; - status = serializeRepr.deserialize(this->m_userData, receivedSize, omitLength); + status = serializeRepr.deserialize(this->m_userData, receivedSize, Fw::Serialization::OMIT_LENGTH); if (receivedSize != requestedSize) { status = Fw::FW_DESERIALIZE_SIZE_MISMATCH; } @@ -111,8 +110,8 @@ void DpContainer::serializeHeader() { status = serializeRepr.serialize(this->m_procTypes); FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast(status)); // Serialize the user data - const bool omitLength = true; - status = serializeRepr.serialize(this->m_userData, sizeof this->m_userData, omitLength); + status = serializeRepr.serialize(this->m_userData, static_cast(sizeof this->m_userData), + Fw::Serialization::OMIT_LENGTH); FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast(status)); // Serialize the data product state status = serializeRepr.serialize(this->m_dpState); diff --git a/Fw/SerializableFile/CMakeLists.txt b/Fw/SerializableFile/CMakeLists.txt index e040d1acba..339ce0bb16 100644 --- a/Fw/SerializableFile/CMakeLists.txt +++ b/Fw/SerializableFile/CMakeLists.txt @@ -12,13 +12,10 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/SerializableFile.cpp" ) set(MOD_DEPS - Fw/Cfg - Fw/Types + Fw/Cfg + Fw/Types + Os ) -# For shared libraries, Os must exist in the module list -if (BUILD_SHARED_LIBS) - list(APPEND MOD_DEPS "Os") -endif() register_fprime_module() ### UTs ### set(UT_SOURCE_FILES diff --git a/Fw/SerializableFile/SerializableFile.cpp b/Fw/SerializableFile/SerializableFile.cpp index afbde7f128..9d469a3ca2 100644 --- a/Fw/SerializableFile/SerializableFile.cpp +++ b/Fw/SerializableFile/SerializableFile.cpp @@ -39,9 +39,9 @@ namespace Fw { return FILE_OPEN_ERROR; } - NATIVE_INT_TYPE capacity = this->m_buffer.getBuffCapacity(); - NATIVE_INT_TYPE length = capacity; - status = file.read(this->m_buffer.getBuffAddr(), length, false); + FwSignedSizeType capacity = static_cast(this->m_buffer.getBuffCapacity()); + FwSignedSizeType length = static_cast(capacity); + status = file.read(this->m_buffer.getBuffAddr(), length, Os::File::WaitType::NO_WAIT); if( Os::File::OP_OK != status ) { file.close(); return FILE_READ_ERROR; @@ -72,12 +72,10 @@ namespace Fw { return FILE_OPEN_ERROR; } - NATIVE_INT_TYPE length = this->m_buffer.getBuffLength(); - NATIVE_INT_TYPE size = length; + FwSignedSizeType length = static_cast(this->m_buffer.getBuffLength()); + FwSignedSizeType size = length; status = file.write(this->m_buffer.getBuffAddr(), length); - if( (Os::File::OP_OK != status) || - (length != size) ) - { + if( (Os::File::OP_OK != status) || (length != size)) { file.close(); return FILE_WRITE_ERROR; } diff --git a/Fw/Types/Serializable.cpp b/Fw/Types/Serializable.cpp index f7f17dd34b..3974f73232 100644 --- a/Fw/Types/Serializable.cpp +++ b/Fw/Types/Serializable.cpp @@ -234,10 +234,18 @@ namespace Fw { } + SerializeStatus SerializeBufferBase::serialize(const U8* buff, NATIVE_UINT_TYPE length) { + return this->serialize(buff, static_cast(length), Serialization::INCLUDE_LENGTH); + } + SerializeStatus SerializeBufferBase::serialize(const U8* buff, NATIVE_UINT_TYPE length, bool noLength) { + return this->serialize(buff, static_cast(length), noLength ? Serialization::OMIT_LENGTH : Serialization::INCLUDE_LENGTH); + } + + SerializeStatus SerializeBufferBase::serialize(const U8* buff, FwSizeType length, Fw::Serialization::t mode) { // First serialize length SerializeStatus stat; - if (not noLength) { + if (mode == Serialization::INCLUDE_LENGTH) { stat = this->serialize(static_cast(length)); if (stat != FW_SERIALIZE_OK) { return stat; @@ -499,11 +507,25 @@ namespace Fw { return FW_SERIALIZE_OK; } + SerializeStatus SerializeBufferBase::deserialize(U8* buff, NATIVE_UINT_TYPE& length) { + FwSizeType length_in_out = static_cast(length); + SerializeStatus status = this->deserialize(buff, length_in_out, Serialization::INCLUDE_LENGTH); + length = static_cast(length_in_out); + return status; + } + SerializeStatus SerializeBufferBase::deserialize(U8* buff, NATIVE_UINT_TYPE& length, bool noLength) { + FwSizeType length_in_out = static_cast(length); + SerializeStatus status = this->deserialize(buff, length_in_out, noLength ? Serialization::OMIT_LENGTH : Serialization::INCLUDE_LENGTH); + length = static_cast(length_in_out); + return status; + } + + SerializeStatus SerializeBufferBase::deserialize(U8* buff, FwSizeType& length, Serialization::t mode) { FW_ASSERT(this->getBuffAddr()); - if (not noLength) { + if (mode == Serialization::INCLUDE_LENGTH) { FwBuffSizeType storedLength; SerializeStatus stat = this->deserialize(storedLength); @@ -519,7 +541,7 @@ namespace Fw { (void) memcpy(buff, &this->getBuffAddr()[this->m_deserLoc], storedLength); - length = static_cast(storedLength); + length = static_cast(storedLength); } else { // make sure enough is left diff --git a/Fw/Types/Serializable.hpp b/Fw/Types/Serializable.hpp index 393935ec59..cf595562e6 100644 --- a/Fw/Types/Serializable.hpp +++ b/Fw/Types/Serializable.hpp @@ -6,6 +6,7 @@ #endif #include +#include "Fw/Deprecate.hpp" namespace Fw { @@ -38,6 +39,14 @@ namespace Fw { virtual ~Serializable(); //!< destructor }; + class Serialization { + public: + enum t { + INCLUDE_LENGTH, //!< Include length as first token in serialization + OMIT_LENGTH //!< Omit length from serialization + }; + }; + class SerializeBufferBase { public: @@ -70,7 +79,21 @@ namespace Fw { SerializeStatus serialize(const void* val); //!< serialize pointer (careful, only pointer value, not contents are serialized) - SerializeStatus serialize(const U8* buff, NATIVE_UINT_TYPE length, bool noLength = false); //!< serialize data buffer + //! serialize data buffer + SerializeStatus serialize(const U8* buff, NATIVE_UINT_TYPE length, bool noLength); + //! serialize data buffer + SerializeStatus serialize(const U8* buff, NATIVE_UINT_TYPE length); + + //! \brief serialize a byte buffer of a given length + //! + //! Serialize bytes from `buff` up to `length`. If `serializationMode` is set to `INCLUDE_LENGTH` then the + //! length is included as the first token. Length may be omitted with `OMIT_LENGTH`. + //! + //! \param buff: buffer to serialize + //! \param length: length of data to serialize + //! \param mode: serialization type + //! \return status of serialization + SerializeStatus serialize(const U8* buff, FwSizeType length, Serialization::t mode); SerializeStatus serialize(const SerializeBufferBase& val); //!< serialize a serialized buffer @@ -102,11 +125,23 @@ namespace Fw { SerializeStatus deserialize(void*& val); //!< deserialize point value (careful, pointer value only, not contents) - // length should be set to max, returned value is actual size stored. If noLength - // is true, use the length variable as the actual number of bytes to deserialize - SerializeStatus deserialize(U8* buff, NATIVE_UINT_TYPE& length, bool noLength = false); //!< deserialize data buffer - // serialize/deserialize Serializable - + //! deserialize data buffer + SerializeStatus deserialize(U8* buff, NATIVE_UINT_TYPE& length, bool noLength); + + //! deserialize data buffer + SerializeStatus deserialize(U8* buff, NATIVE_UINT_TYPE& length); + //! \brief deserialize a byte buffer of a given length + //! + //! Deserialize bytes into `buff` of `length` bytes. If `serializationMode` is set to `INCLUDE_LENGTH` then + //! the length is deserialized first followed by the bytes. Length may be omitted with `OMIT_LENGTH` and + //! in this case `length` bytes will be deserialized. `length` will be filled with the amount of data + //! deserialized. + //! + //! \param buff: buffer to hold deserialized data + //! \param length: length of data to deserialize length is filled with deserialized length + //! \param mode: deserialization type + //! \return status of serialization + SerializeStatus deserialize(U8* buff, FwSizeType& length, Serialization::t mode); SerializeStatus deserialize(Serializable &val); //!< deserialize an object derived from serializable base class diff --git a/Os/CMakeLists.txt b/Os/CMakeLists.txt index b609ec0b49..5f5147f115 100644 --- a/Os/CMakeLists.txt +++ b/Os/CMakeLists.txt @@ -5,6 +5,15 @@ # MOD_DEPS: (optional) module dependencies # #### +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Models") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Stub") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Posix") + +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/file/SyntheticFileSystem.cpp" +) +register_fprime_module(Os_Test_File_SyntheticFileSystem) + # Basic module dependencies set(MOD_DEPS @@ -26,7 +35,9 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/MemCommon.cpp" "${CMAKE_CURRENT_LIST_DIR}/ValidateFileCommon.cpp" "${CMAKE_CURRENT_LIST_DIR}/ValidatedFile.cpp" - "${CMAKE_CURRENT_LIST_DIR}/FileCommon.cpp" + + # Refactored common files + "${CMAKE_CURRENT_LIST_DIR}/File.cpp" ) # Check for default logger if (NOT FPRIME_DISABLE_DEFAULT_LOGGER) @@ -42,7 +53,6 @@ if (FPRIME_USE_POSIX) "${CMAKE_CURRENT_LIST_DIR}/Pthreads/BufferQueueCommon.cpp" "${CMAKE_CURRENT_LIST_DIR}/Pthreads/PriorityBufferQueue.cpp" "${CMAKE_CURRENT_LIST_DIR}/Pthreads/MaxHeap/MaxHeap.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Linux/File.cpp" "${CMAKE_CURRENT_LIST_DIR}/Posix/Task.cpp" "${CMAKE_CURRENT_LIST_DIR}/Linux/InterruptLock.cpp" "${CMAKE_CURRENT_LIST_DIR}/Linux/WatchdogTimer.cpp" @@ -90,20 +100,16 @@ if (FPRIME_USE_BAREMETAL_SCHEDULER) list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Task.cpp") endif() register_fprime_module() - -# Add stubs directory for testing builds -if (BUILD_TESTING) - add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Stubs/") -endif() +require_fprime_implementation(Os/File) ### UTS ### Note: 3 separate UTs registered here. set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsQueueTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsTestMain.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/IntervalTimerTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsFileSystemTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsValidateFileTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsTaskTest.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsFileSystemTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsSystemResourcesTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsMutexBasicLockableTest.cpp" ) diff --git a/Os/File.cpp b/Os/File.cpp new file mode 100644 index 0000000000..95fd8c3ff9 --- /dev/null +++ b/Os/File.cpp @@ -0,0 +1,233 @@ +// ====================================================================== +// \title Os/File.cpp +// \brief common function implementation for Os::File +// ====================================================================== +#include +#include + +extern "C" { +#include // borrow CRC +} +namespace Os { + +File::File() : m_crc_buffer(), m_handle_storage(), m_delegate(*FileInterface::getDelegate(&m_handle_storage[0])) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); +} + +File::~File() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + if (this->m_mode != OPEN_NO_MODE) { + this->close(); + } + m_delegate.~FileInterface(); +} + +File::File(const File& other) : + m_mode(other.m_mode), + m_path(other.m_path), + m_crc(other.m_crc), + m_crc_buffer(), + m_handle_storage(), + m_delegate(*FileInterface::getDelegate(&m_handle_storage[0], &other.m_delegate)) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); +} + +File& File::operator=(const File& other) { + if (this != &other) { + this->m_mode = other.m_mode; + this->m_path = other.m_path; + this->m_crc = other.m_crc; + this->m_delegate = *FileInterface::getDelegate(&m_handle_storage[0], &other.m_delegate); + } + return *this; +} + +File::Status File::open(const CHAR* filepath, File::Mode requested_mode) { + return this->open(filepath, requested_mode, OverwriteType::NO_OVERWRITE); +} + +File::Status File::open(const CHAR* filepath, File::Mode requested_mode, File::OverwriteType overwrite) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(nullptr != filepath); + FW_ASSERT(File::Mode::OPEN_NO_MODE < requested_mode && File::Mode::MAX_OPEN_MODE > requested_mode); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + FW_ASSERT((0 <= overwrite) && (overwrite < OverwriteType::MAX_OVERWRITE_TYPE)); + // Check for already opened file + if (this->isOpen()) { + return File::Status::INVALID_MODE; + } + File::Status status = this->m_delegate.open(filepath, requested_mode, overwrite); + if (status == File::Status::OP_OK) { + this->m_mode = requested_mode; + this->m_path = filepath; + } + // Reset any open CRC calculations + this->m_crc = File::INITIAL_CRC; + return status; +} + +void File::close() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + this->m_delegate.close(); + this->m_mode = Mode::OPEN_NO_MODE; + this->m_path = nullptr; +} + +bool File::isOpen() const { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + return this->m_mode != Mode::OPEN_NO_MODE; +} + +File::Status File::size(FwSignedSizeType& size_result) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + if (OPEN_NO_MODE == this->m_mode) { + return File::Status::NOT_OPENED; + } + return this->m_delegate.size(size_result); +} + +File::Status File::position(FwSignedSizeType &position_result) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + return File::Status::NOT_OPENED; + } + return this->m_delegate.position(position_result); +} + +File::Status File::preallocate(FwSignedSizeType offset, FwSignedSizeType length) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(offset >= 0); + FW_ASSERT(length >= 0); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + return File::Status::NOT_OPENED; + } else if (OPEN_READ == this->m_mode) { + return File::Status::INVALID_MODE; + } + return this->m_delegate.preallocate(offset, length); +} + +File::Status File::seek(FwSignedSizeType offset, File::SeekType seekType) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT((0 <= seekType) && (seekType < SeekType::MAX_SEEK_TYPE)); + // Cannot do a seek with a negative offset in absolute mode + FW_ASSERT((seekType == File::SeekType::RELATIVE) || (offset >= 0)); + FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE)); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + return File::Status::NOT_OPENED; + } + return this->m_delegate.seek(offset, seekType); +} + +File::Status File::flush() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + return File::Status::NOT_OPENED; + } else if (OPEN_READ == this->m_mode) { + return File::Status::INVALID_MODE; + } + return this->m_delegate.flush(); +} + +File::Status File::read(U8* buffer, FwSignedSizeType &size) { + return this->read(buffer, size, WaitType::WAIT); +} + +File::Status File::read(U8* buffer, FwSignedSizeType &size, File::WaitType wait) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(buffer != nullptr); + FW_ASSERT(size >= 0); + FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + size = 0; + return File::Status::NOT_OPENED; + } else if (OPEN_READ != this->m_mode) { + size = 0; + return File::Status::INVALID_MODE; + } + return this->m_delegate.read(buffer, size, wait); +} + +File::Status File::write(const U8* buffer, FwSignedSizeType &size) { + return this->write(buffer, size, WaitType::WAIT); +} + + +File::Status File::write(const U8* buffer, FwSignedSizeType &size, File::WaitType wait) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(buffer != nullptr); + FW_ASSERT(size >= 0); + FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (OPEN_NO_MODE == this->m_mode) { + size = 0; + return File::Status::NOT_OPENED; + } else if (OPEN_READ == this->m_mode) { + size = 0; + return File::Status::INVALID_MODE; + } + return this->m_delegate.write(buffer, size, wait); +} + +FileHandle* File::getHandle() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + return this->m_delegate.getHandle(); +} + +File::Status File::calculateCrc(U32 &crc) { + File::Status status = File::Status::OP_OK; + FwSignedSizeType size = FW_FILE_CHUNK_SIZE; + crc = 0; + for (FwSizeType i = 0; i < std::numeric_limits::max(); i++) { + status = this->incrementalCrc(size); + // Break on eof or error + if ((size != FW_FILE_CHUNK_SIZE) || (status != File::OP_OK)) { + break; + } + } + // When successful, finalize the CRC + if (status == File::OP_OK) { + status = this->finalizeCrc(crc); + } + return status; +} + +File::Status File::incrementalCrc(FwSignedSizeType &size) { + File::Status status = File::Status::OP_OK; + FW_ASSERT(size >= 0); + FW_ASSERT(size <= FW_FILE_CHUNK_SIZE); + if (OPEN_NO_MODE == this->m_mode) { + status = File::Status::NOT_OPENED; + } else if (OPEN_READ != this->m_mode) { + status = File::Status::INVALID_MODE; + } else { + // Read data without waiting for additional data to be available + status = this->read(this->m_crc_buffer, size, File::WaitType::NO_WAIT); + if (OP_OK == status) { + for (FwSignedSizeType i = 0; i < size && i < FW_FILE_CHUNK_SIZE; i++) { + this->m_crc = update_crc_32(this->m_crc, static_cast(this->m_crc_buffer[i])); + } + } + } + return status; +} + +File::Status File::finalizeCrc(U32 &crc) { + File::Status status = File::Status::OP_OK; + crc = this->m_crc; + this->m_crc = File::INITIAL_CRC; + return status; +} +} // Os + diff --git a/Os/File.hpp b/Os/File.hpp index f0082209fa..84695cf3a9 100644 --- a/Os/File.hpp +++ b/Os/File.hpp @@ -1,26 +1,31 @@ -#ifndef _File_hpp_ -#define _File_hpp_ +// ====================================================================== +// \title Os/File.hpp +// \brief common function definitions for Os::File +// ====================================================================== +#ifndef Os_File_hpp_ +#define Os_File_hpp_ #include namespace Os { + //! \brief base implementation of FileHandle + //! + struct FileHandle {}; // This class encapsulates a very simple file interface that has the most often-used features - - class File { + class FileInterface { public: - - typedef enum { + enum Mode { OPEN_NO_MODE, //!< File mode not yet selected OPEN_READ, //!< Open file for reading + OPEN_CREATE, //!< Open file for writing and truncates file if it exists, ie same flags as creat() OPEN_WRITE, //!< Open file for writing OPEN_SYNC_WRITE, //!< Open file for writing; writes don't return until data is on disk - OPEN_SYNC_DIRECT_WRITE, //!< Open file for writing, bypassing all caching. Requires data alignment - OPEN_CREATE, //!< Open file for writing and truncates file if it exists, ie same flags as creat() - OPEN_APPEND, //!< Open file for appending - } Mode; + OPEN_APPEND, //!< Open file for appending + MAX_OPEN_MODE //!< Maximum value of mode + }; - typedef enum { + enum Status { OP_OK, //!< Operation was successful DOESNT_EXIST, //!< File doesn't exist (for read) NO_SPACE, //!< No space left @@ -28,39 +33,460 @@ namespace Os { BAD_SIZE, //!< Invalid size parameter NOT_OPENED, //!< file hasn't been opened yet FILE_EXISTS, //!< file already exist (for CREATE with O_EXCL enabled) + NOT_SUPPORTED, //!< Kernel or file system does not support operation + INVALID_MODE, //!< Mode for file access is invalid for current operation + INVALID_ARGUMENT, //!< Invalid argument passed in OTHER_ERROR, //!< A catch-all for other errors. Have to look in implementation-specific code - } Status; + MAX_STATUS //!< Maximum value of status + }; + + enum OverwriteType { + NO_OVERWRITE, //!< Do NOT overwrite existing files + OVERWRITE, //!< Overwrite file when it exists and creation was requested + MAX_OVERWRITE_TYPE + }; + + enum SeekType { + RELATIVE, //!< Relative seek from current file offset + ABSOLUTE, //!< Absolute seek from beginning of file + MAX_SEEK_TYPE + }; + + enum WaitType { + NO_WAIT, //!< Do not wait for read/write operation to finish + WAIT, //!< Do wait for read/write operation to finish + MAX_WAIT_TYPE + }; + + virtual ~FileInterface() = default; + + //! \brief open file with supplied path and mode + //! + //! Open the file passed in with the given mode. If overwrite is set to OVERWRITE, then opening files in + //! OPEN_CREATE mode will clobber existing files. Set overwrite to NO_OVERWRITE to preserve existing files. + //! The status of the open request is returned from the function call. Delegates to the chosen + //! implementation's `open` function. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! It is invalid to supply `overwrite` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \param overwrite: overwrite existing file on create + //! \return: status of the open + //! + virtual Status open(const char* path, Mode mode, OverwriteType overwrite) = 0; + + //! \brief close the file, if not opened then do nothing + //! + //! Closes the file, if open. Otherwise this function does nothing. Delegates to the chosen implementation's + //! `closeInternal` function. `mode` is set to `OPEN_NO_MODE`. + //! + virtual void close() = 0; + + //! \brief get size of currently open file + //! + //! Get the size of the currently open file and fill the size parameter. Return status of the operation. + //! \param size: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + virtual Status size(FwSignedSizeType& size_result) = 0; - File(); //!< Constructor - virtual ~File(); //!< Destructor. Will close file if still open - Status prealloc(NATIVE_INT_TYPE offset, NATIVE_INT_TYPE len); - Status open(const char* fileName, Mode mode); //!< open file. Writing creates file if it doesn't exist - Status open(const char* fileName, Mode mode, bool include_excl); //!< open file. Writing creates file if it doesn't exist - bool isOpen(); //!< check if file descriptor is open or not. - Status seek(NATIVE_INT_TYPE offset, bool absolute = true); //!< seek to location. If absolute = true, absolute from beginning of file - Status flush(); //!< flush data to disk. No-op on systems that do not support. - Status read(void * buffer, NATIVE_INT_TYPE &size, bool waitForFull = true); //!< read data from file; returns amount read or errno. - //!< waitForFull = true to wait for all bytes to be read - // size is modified to actual read size - Status write(const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone = true); //!< write size; will return amount written or errno - Status bulkWrite(const void * buffer, NATIVE_UINT_TYPE &totalSize, NATIVE_INT_TYPE chunkSize); //!< write size; will return amount written or errno + //! \brief get file pointer position of the currently open file + //! + //! Get the current position of the read/write pointer of the open file. + //! \param position: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + virtual Status position(FwSignedSizeType& position_result) = 0; - void close(); //!< close file + //! \brief pre-allocate file storage + //! + //! Pre-allocates file storage with at least `length` storage starting at `offset`. No-op on implementations + //! that cannot pre-allocate. + //! + //! It is invalid to pass a negative `offset`. + //! It is invalid to pass a negative `length`. + //! + //! \param offset: offset into file + //! \param length: length after offset to preallocate + //! \return OP_OK on success otherwise error status + //! + virtual Status preallocate(FwSignedSizeType offset, FwSignedSizeType length) = 0; - NATIVE_INT_TYPE getLastError(); //!< read back last error code (typically errno) - const char* getLastErrorString(); //!< get a string of the last error (typically from strerror) - Status calculateCRC32(U32 &crc); //!< calculates the CRC32 of the file + //! \brief seek the file pointer to the given offset + //! + //! Seek the file pointer to the given `offset`. If `seekType` is set to `ABSOLUTE` then the offset is calculated + //! from the start of the file, and if it is set to `CURRENT` it is calculated from the current position. + //! + //! \param offset: offset to seek to + //! \param seekType: `ABSOLUTE` for seeking from beginning of file, `CURRENT` to use current position. + //! \return OP_OK on success otherwise error status + //! + virtual Status seek(FwSignedSizeType offset, SeekType seekType) = 0; - static Status niceCRC32(U32 &crc, const char* fileName); //!< Calculates CRC32 of file, not burdening FS + //! \brief flush file contents to storage + //! + //! Flushes the file contents to storage (i.e. out of the OS cache to disk). Does nothing in implementations + //! that do not support flushing. + //! + //! \return OP_OK on success otherwise error status + //! + virtual Status flush() = 0; - private: + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Read data from this file up to the `size` and store it in `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been read successfully read or the end of the file has been + //! reached. When `wait` is set to `NO_WAIT` it will return whatever data is currently available. + //! + //! `size` will be updated to the count of bytes actually read. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + virtual Status read(U8* buffer, FwSignedSizeType &size, WaitType wait) = 0; - NATIVE_INT_TYPE m_fd; //!< Stored file descriptor - Mode m_mode; //!< Stores mode for error checking - NATIVE_INT_TYPE m_lastError; //!< stores last error + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Write data to this file up to the `size` from the `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been written successfully to disk. When `wait` is set to + //! `NO_WAIT` it will return once the data is sent to the OS. + //! + //! `size` will be updated to the count of bytes actually written. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data to write to disk, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + virtual Status write(const U8* buffer, FwSignedSizeType &size, WaitType wait) = 0; + //! \brief returns the raw file handle + //! + //! Gets the raw file handle from the implementation. Note: users must include the implementation specific + //! header to make any real use of this handle. Otherwise it//!must* be passed as an opaque type. + //! + //! \return raw file handle + //! + virtual FileHandle* getHandle() = 0; + + //! \brief provide a pointer to a file delegate object + //! + //! This function must return a pointer to a `FileInterface` object that contains the real implementation of the file + //! functions as defined by the implementor. This function must do several things to be considered correctly + //! implemented: + //! + //! 1. Assert that the supplied memory is non-null. e.g `FW_ASSERT(aligned_placement_new_memory != NULL);` + //! 2. Assert that their implementation fits within FW_HANDLE_MAX_SIZE. + //! e.g. `static_assert(sizeof(PosixFileImplementation) <= sizeof Os::File::m_handle_storage, + //! "FW_HANDLE_MAX_SIZE too small");` + //! 3. Assert that their implementation aligns within FW_HANDLE_ALIGNMENT. + //! e.g. `static_assert((FW_HANDLE_ALIGNMENT % alignof(PosixFileImplementation)) == 0, "Bad handle alignment");` + //! 4. If to_copy is null, placement new their implementation into `aligned_placement_new_memory` + //! e.g. `FileInterface* interface = new (aligned_placement_new_memory) PosixFileImplementation;` + //! 5. If to_copy is non-null, placement new using copy constructor their implementation into + //! `aligned_placement_new_memory` + //! e.g. `FileInterface* interface = new (aligned_placement_new_memory) PosixFileImplementation(*to_copy);` + //! 6. Return the result of the placement new + //! e.g. `return interface;` + //! + //! \return result of placement new, must be equivalent to `aligned_placement_new_memory` + //! + static FileInterface* getDelegate(U8* aligned_placement_new_memory, const FileInterface* to_copy=nullptr); }; -} + class File final : public FileInterface { + public: + // Required for access to m_handle_storage for static assertions against actual storage + friend FileInterface* FileInterface::getDelegate(U8*, const FileInterface*); + //! \brief constructor + //! + File(); + //! \brief destructor + //! + ~File() final; + + //! \brief copy constructor that copies the internal representation + File(const File& other); + + //! \brief assignment operator that copies the internal representation + File& operator=(const File& other); + + //! \brief determine if the file is open + //! \return true if file is open, false otherwise + //! + bool isOpen() const; + + // ------------------------------------ + // Functions supplying default values + // ------------------------------------ + + //! \brief open file with supplied path and mode + //! + //! Open the file passed in with the given mode. Opening files with `OPEN_CREATE` mode will not clobber existing + //! files. Use other `open` method to set overwrite flag and clobber existing files. The status of the open + //! request is returned from the function call. Delegates to the chosen implementation's `open` function. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \return: status of the open + //! + Os::FileInterface::Status open(const char* path, Mode mode); + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Read data from this file up to the `size` and store it in `buffer`. This version will + //! will block until the requested size has been read successfully read or the end of the file has been + //! reached. + //! + //! `size` will be updated to the count of bytes actually read. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \return OP_OK on success otherwise error status + //! + Status read(U8* buffer, FwSignedSizeType &size); + + //! \brief write data to this file from the supplied buffer bounded by size + //! + //! Write data from `buffer` up to the `size` and store it in this file. This call + //! will block until the requested size has been written. Otherwise, this call will write without blocking. + //! + //! `size` will be updated to the count of bytes actually written. Status will reflect the success/failure of + //! the write operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! + //! \param buffer: memory location of data to write to file + //! \param size: size of data to write + //! \return OP_OK on success otherwise error status + //! + Status write(const U8* buffer, FwSignedSizeType &size); + + + // ------------------------------------ + // Functions overrides + // ------------------------------------ + + //! \brief open file with supplied path and mode + //! + //! Open the file passed in with the given mode. If overwrite is set to OVERWRITE, then opening files in + //! OPEN_CREATE mode will clobber existing files. Set overwrite to NO_OVERWRITE to preserve existing files. + //! The status of the open request is returned from the function call. Delegates to the chosen + //! implementation's `open` function. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! It is invalid to supply `overwrite` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \param overwrite: overwrite existing file on create + //! \return: status of the open + //! + Os::FileInterface::Status open(const char* path, Mode mode, OverwriteType overwrite) override; + + //! \brief close the file, if not opened then do nothing + //! + //! Closes the file, if open. Otherwise this function does nothing. Delegates to the chosen implementation's + //! `closeInternal` function. `mode` is set to `OPEN_NO_MODE`. + //! + void close() override; + + //! \brief get size of currently open file + //! + //! Get the size of the currently open file and fill the size parameter. Return status of the operation. + //! \param size: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status size(FwSignedSizeType& size_result) override; + + //! \brief get file pointer position of the currently open file + //! + //! Get the current position of the read/write pointer of the open file. + //! \param position: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status position(FwSignedSizeType& position_result) override; + + //! \brief pre-allocate file storage + //! + //! Pre-allocates file storage with at least `length` storage starting at `offset`. No-op on implementations + //! that cannot pre-allocate. + //! + //! It is invalid to pass a negative `offset`. + //! It is invalid to pass a negative `length`. + //! + //! \param offset: offset into file + //! \param length: length after offset to preallocate + //! \return OP_OK on success otherwise error status + //! + Status preallocate(FwSignedSizeType offset, FwSignedSizeType length) override; + + //! \brief seek the file pointer to the given offset + //! + //! Seek the file pointer to the given `offset`. If `seekType` is set to `ABSOLUTE` then the offset is calculated + //! from the start of the file, and if it is set to `CURRENT` it is calculated from the current position. + //! + //! \param offset: offset to seek to + //! \param seekType: `ABSOLUTE` for seeking from beginning of file, `CURRENT` to use current position. + //! \return OP_OK on success otherwise error status + //! + Status seek(FwSignedSizeType offset, SeekType seekType) override; + + //! \brief flush file contents to storage + //! + //! Flushes the file contents to storage (i.e. out of the OS cache to disk). Does nothing in implementations + //! that do not support flushing. + //! + //! \return OP_OK on success otherwise error status + //! + Status flush() override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Read data from this file up to the `size` and store it in `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been read successfully read or the end of the file has been + //! reached. When `wait` is set to `NO_WAIT` it will return whatever data is currently available. + //! + //! `size` will be updated to the count of bytes actually read. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status read(U8* buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Write data to this file up to the `size` from the `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been written successfully to disk. When `wait` is set to + //! `NO_WAIT` it will return once the data is sent to the OS. + //! + //! `size` will be updated to the count of bytes actually written. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data to write to disk, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status write(const U8* buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief returns the raw file handle + //! + //! Gets the raw file handle from the implementation. Note: users must include the implementation specific + //! header to make any real use of this handle. Otherwise it//!must* be passed as an opaque type. + //! + //! \return raw file handle + //! + FileHandle* getHandle() override; + + //! \brief calculate the CRC32 of the entire file + //! + //! Calculates the CRC32 of the file's contents. The `crc` parameter will be updated to contain the CRC or 0 on + //! failure. Status will represent failure conditions. This call will be decomposed into calculations on + //! sections of the file `FW_FILE_CHUNK_SIZE` bytes long. + //! + //! This function requires that the file already be opened for "READ" mode. + //! + //! On error crc will be set to 0. + //! + //! This function is equivalent to the following pseudo-code: + //! + //! ``` + //! U32 crc; + //! do { + //! size = FW_FILE_CHUNK_SIZE; + //! m_file.incrementalCrc(size); + //! while (size == FW_FILE_CHUNK_SIZE); + //! m_file.finalize(crc); + //! ``` + //! \param crc: U32 bit value to fill with CRC + //! \return OP_OK on success otherwise error status + //! + Status calculateCrc(U32& crc); + + //! \brief calculate the CRC32 of the next section of data + //! + //! Starting at the current file pointer, this will add `size` bytes of data to the currently calculated CRC. + //! Call `finalizeCrc` to retrieve the CRC or `calculateCrc` to perform a CRC on the entire file. This call will + //! not block waiting for data on the underlying read, nor will it reset the file position pointer. On error, + //! the current CRC results should be discarded by reopening the file or calling `finalizeCrc` and + //! discarding its result. `size` will be updated with the `size` actually read and used in the CRC calculation. + //! + //! This function requires that the file already be opened for "READ" mode. + //! + //! It is illegal for size to be less than or equal to 0 or greater than FW_FILE_CHUNK_SIZE. + //! + //! \param size: size of data to read for CRC + //! \return: status of the CRC calculation + //! + Status incrementalCrc(FwSignedSizeType& size); + + //! \brief finalize and retrieve the CRC value + //! + //! Finalizes the CRC computation and returns the CRC value. The `crc` value will be modified to contain the + //! crc or 0 on error. Note: this will reset any active CRC calculation and effectively re-initializes any + //! `incrementalCrc` calculation. + //! + //! On error crc will be set to 0. + //! + //! \param crc: value to fill + //! \return status of the CRC calculation + //! + Status finalizeCrc(U32& crc); + + private: + + + PRIVATE: + static const U32 INITIAL_CRC = 0xFFFFFFFF; //!< Initial value for CRC calculation + Mode m_mode = Mode::OPEN_NO_MODE; //!< Stores mode for error checking + const CHAR* m_path = nullptr; //!< Path last opened + + U32 m_crc = File::INITIAL_CRC; //!< Current CRC calculation + U8 m_crc_buffer[FW_FILE_CHUNK_SIZE]; + + // This section is used to store the implementation-defined file handle. To Os::File and fprime, this type is + // opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store then handle in + // the byte-array here and set `handle` to that address for storage. + // + alignas(FW_HANDLE_ALIGNMENT) U8 m_handle_storage[FW_HANDLE_MAX_SIZE]; //!< Storage for aligned FileHandle data + FileInterface& m_delegate; //!< Delegate for the real implementation + }; +} #endif diff --git a/Os/FileCommon.cpp b/Os/FileCommon.cpp deleted file mode 100644 index 47189248e9..0000000000 --- a/Os/FileCommon.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -#include // borrow CRC - -#ifdef __cplusplus -} -#endif // __cplusplus - -namespace Os { - File::Status File::niceCRC32(U32 &crc, const char* fileName) - { - //Constants used in this function - const U32 CHUNK_SIZE = 4096; - const U32 INITIAL_SEED = 0xFFFFFFFF; - const U32 MAX_IT = 0xFFFFFFFF; //Max int for U32 - //Loop variables for calculating CRC - NATIVE_INT_TYPE offset = 0; - U32 seed = INITIAL_SEED; - Status status; - File file; - U8 file_buffer[CHUNK_SIZE]; - bool eof = false; - //Loop across the whole file - for (U32 i = 0; !eof && i < MAX_IT; i++) { - //Open and check status - status = file.open(fileName, OPEN_READ); - if (status != OP_OK) { - crc = 0; - return status; - } - //Find our place - status = file.seek(offset, true); - if (status != OP_OK) { - crc = 0; - return status; - } - NATIVE_INT_TYPE chunk = CHUNK_SIZE; - //Read data and check status - status = file.read(file_buffer, chunk, false); - offset += chunk; - //Close file, then update CRC. This reduces time file is required open - file.close(); - if (chunk != 0 && status == OP_OK) { - for (U32 ci = 0; static_cast(ci) < chunk; ci++) { - seed = update_crc_32(seed, file_buffer[ci]); - } - } else if (chunk == 0 && status == OP_OK) { - eof = true; - break; - } else { - crc = 0; - return status; - } - } - //Reach max-loop - if (!eof) { - crc = 0; - return OTHER_ERROR; - } - //Good CRC - crc = seed; - return OP_OK; - } -} diff --git a/Os/FileSystem.hpp b/Os/FileSystem.hpp index e6b0e2e5f3..00ce1a3575 100644 --- a/Os/FileSystem.hpp +++ b/Os/FileSystem.hpp @@ -32,10 +32,10 @@ namespace Os { Status moveFile(const char* originPath, const char* destPath); //! moves a file from origin to destination Status copyFile(const char* originPath, const char* destPath); //! copies a file from origin to destination Status appendFile(const char* originPath, const char* destPath, bool createMissingDest=false); //! append file origin to destination file. If boolean true, creates a brand new file if the destination doesn't exist. - Status getFileSize(const char* path, FwSizeType& size); //!< gets the size of the file (in bytes) at location path + Status getFileSize(const char* path, FwSignedSizeType& size); //!< gets the size of the file (in bytes) at location path Status getFileCount(const char* directory, U32& fileCount); //!< counts the number of files in the given directory Status changeWorkingDirectory(const char* path); //!< move current directory to path - Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes); //!< get FS free and total space in bytes on filesystem containing path + Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes); //!< get FS free and total space in bytes on filesystem containing path } } diff --git a/Os/Linux/FileSystem.cpp b/Os/Linux/FileSystem.cpp index 8f130e658c..1150f68aea 100644 --- a/Os/Linux/FileSystem.cpp +++ b/Os/Linux/FileSystem.cpp @@ -317,19 +317,19 @@ Status initAndCheckFileStats(const char* filePath, struct stat* fileInfo = nullp * @param destination File to copy data to * @param size The number of bytes to copy */ -Status copyFileData(File source, File destination, FwSizeType size) { +Status copyFileData(File& source, File& destination, FwSignedSizeType size) { static_assert(FILE_SYSTEM_CHUNK_SIZE != 0, "FILE_SYSTEM_CHUNK_SIZE must be >0"); U8 fileBuffer[FILE_SYSTEM_CHUNK_SIZE]; File::Status file_status; // Set loop limit - const FwSizeType copyLoopLimit = (size / FILE_SYSTEM_CHUNK_SIZE) + 2; + const FwSignedSizeType copyLoopLimit = (size / FILE_SYSTEM_CHUNK_SIZE) + 2; - FwSizeType loopCounter = 0; - NATIVE_INT_TYPE chunkSize; + FwSignedSizeType loopCounter = 0; + FwSignedSizeType chunkSize; while (loopCounter < copyLoopLimit) { chunkSize = FILE_SYSTEM_CHUNK_SIZE; - file_status = source.read(&fileBuffer, chunkSize, false); + file_status = source.read(fileBuffer, chunkSize, Os::File::WaitType::NO_WAIT); if (file_status != File::OP_OK) { return handleFileError(file_status); } @@ -339,7 +339,7 @@ Status copyFileData(File source, File destination, FwSizeType size) { break; } - file_status = destination.write(fileBuffer, chunkSize, true); + file_status = destination.write(fileBuffer, chunkSize, Os::File::WaitType::WAIT); if (file_status != File::OP_OK) { return handleFileError(file_status); } @@ -354,7 +354,7 @@ Status copyFile(const char* originPath, const char* destPath) { FileSystem::Status fs_status; File::Status file_status; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; File source; File destination; @@ -392,7 +392,7 @@ Status copyFile(const char* originPath, const char* destPath) { Status appendFile(const char* originPath, const char* destPath, bool createMissingDest) { FileSystem::Status fs_status; File::Status file_status; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; File source; File destination; @@ -435,7 +435,7 @@ Status appendFile(const char* originPath, const char* destPath, bool createMissi return fs_status; } // end appendFile -Status getFileSize(const char* path, FwSizeType& size) { +Status getFileSize(const char* path, FwSignedSizeType& size) { Status fileStat = OP_OK; struct stat fileStatStruct; diff --git a/Os/Models/CMakeLists.txt b/Os/Models/CMakeLists.txt new file mode 100644 index 0000000000..428c495955 --- /dev/null +++ b/Os/Models/CMakeLists.txt @@ -0,0 +1,12 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +# Note: using PROJECT_NAME as EXECUTABLE_NAME +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/File.fpp" +) +register_fprime_module() diff --git a/Os/Models/File.fpp b/Os/Models/File.fpp new file mode 100644 index 0000000000..73fdaf3d6f --- /dev/null +++ b/Os/Models/File.fpp @@ -0,0 +1,30 @@ +# ====================================================================== +# \title Os/Models/file.fpp +# \brief FPP type definitions for Os/File.hpp concepts +# ====================================================================== + +module Os { +@ FPP shadow-enum representing Os::File::Status +enum FileStatus { + OP_OK, @< Operation was successful + DOESNT_EXIST, @< File doesn't exist (for read) + NO_SPACE, @< No space left + NO_PERMISSION, @< No permission to read/write file + BAD_SIZE, @< Invalid size parameter + NOT_OPENED, @< file hasn't been opened yet + FILE_EXISTS, @< file already exist (for CREATE with O_EXCL enabled) + NOT_SUPPORTED, @< Kernel or file system does not support operation + INVALID_MODE, @< Mode for file access is invalid for current operation + INVALID_ARGUMENT, @< Invalid argument passed in + OTHER_ERROR, @< A catch-all for other errors. Have to look in implementation-specific code +} +@ FPP shadow-enum representing Os::File::Mode +enum FileMode { + OPEN_NO_MODE, @< File mode not yet selected + OPEN_READ, @< Open file for reading + OPEN_CREATE, @< Open file for writing and truncates file if it exists, ie same flags as creat() + OPEN_WRITE, @< Open file for writing + OPEN_SYNC_WRITE, @< Open file for writing; writes don't return until data is on disk + OPEN_APPEND, @< Open file for appending +} +} diff --git a/Os/Models/Models.hpp b/Os/Models/Models.hpp new file mode 100644 index 0000000000..2cc9457446 --- /dev/null +++ b/Os/Models/Models.hpp @@ -0,0 +1,56 @@ +// ====================================================================== +// \title Os/Models/Models.hpp +// \brief header used to validate Os/Models before use +// ====================================================================== + +#include "Os/Models/FileStatusEnumAc.hpp" +#include "Os/Models/FileModeEnumAc.hpp" + +#ifndef OS_MODELS_MODELS_HPP +#define OS_MODELS_MODELS_HPP + +// Check consistency of every constant in the Os::File::Status enum +static_assert(static_cast(Os::File::Status::MAX_STATUS) == + static_cast(Os::FileStatus::NUM_CONSTANTS), + "File status and FPP shadow enum have inconsistent number of values"); +static_assert(static_cast(Os::File::Status::OP_OK) == Os::FileStatus::T::OP_OK, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::DOESNT_EXIST) == Os::FileStatus::T::DOESNT_EXIST, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::NO_SPACE) == Os::FileStatus::T::NO_SPACE, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::NO_PERMISSION) == Os::FileStatus::T::NO_PERMISSION, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::BAD_SIZE) == Os::FileStatus::T::BAD_SIZE, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::NOT_OPENED) == Os::FileStatus::T::NOT_OPENED, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::FILE_EXISTS) == Os::FileStatus::T::FILE_EXISTS, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::NOT_SUPPORTED) == Os::FileStatus::T::NOT_SUPPORTED, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::INVALID_MODE) == Os::FileStatus::T::INVALID_MODE, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::INVALID_ARGUMENT) == Os::FileStatus::T::INVALID_ARGUMENT, + "File status and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Status::OTHER_ERROR) == Os::FileStatus::T::OTHER_ERROR, + "File status and FPP shadow enum do not match"); + +// Check consistency of every constant in the Os::File::Mode enum +static_assert(static_cast(Os::File::Mode::MAX_OPEN_MODE) == + static_cast(Os::FileMode::NUM_CONSTANTS), + "File mode and FPP shadow enum have inconsistent number of values"); +static_assert(static_cast(Os::File::Mode::OPEN_NO_MODE) == Os::FileMode::T::OPEN_NO_MODE, + "File mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Mode::OPEN_READ) == Os::FileMode::T::OPEN_READ, + "File mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Mode::OPEN_CREATE) == Os::FileMode::T::OPEN_CREATE, + "File mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Mode::OPEN_WRITE) == Os::FileMode::T::OPEN_WRITE, + "File mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Mode::OPEN_SYNC_WRITE) == Os::FileMode::T::OPEN_SYNC_WRITE, + "File mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::File::Mode::OPEN_APPEND) == Os::FileMode::T::OPEN_APPEND, + "File mode and FPP shadow enum do not Mode"); + +#endif // OS_MODELS_MODELS_HPP diff --git a/Os/Posix/CMakeLists.txt b/Os/Posix/CMakeLists.txt new file mode 100644 index 0000000000..364a8784b9 --- /dev/null +++ b/Os/Posix/CMakeLists.txt @@ -0,0 +1,32 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +#### +restrict_platforms(Linux Darwin) +add_custom_target("${FPRIME_CURRENT_MODULE}") +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/File.cpp" + "${CMAKE_CURRENT_LIST_DIR}/errno.cpp" +) +set(HEADER_FILES + "${CMAKE_CURRENT_LIST_DIR}/errno.hpp" + "${CMAKE_CURRENT_LIST_DIR}/File.hpp" +) +register_fprime_module(Os_File_Posix) +register_fprime_implementation(Os/File Os_File_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultFile.cpp") + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/CommonTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/FileRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixFileTests.cpp" +) +set(UT_MOD_DEPS + Os + STest + Os_Test_File_SyntheticFileSystem +) +choose_fprime_implementation(Os/File Os_File_Posix) +register_fprime_ut(PosixFileTest) diff --git a/Os/Posix/DefaultFile.cpp b/Os/Posix/DefaultFile.cpp new file mode 100644 index 0000000000..4b7d486b27 --- /dev/null +++ b/Os/Posix/DefaultFile.cpp @@ -0,0 +1,34 @@ +// ====================================================================== +// \title Os/Posix/DefaultFile.cpp +// \brief sets default Os::File to posix implementation via linker +// ====================================================================== +#include "Os/File.hpp" +#include "Os/Posix/File.hpp" +#include "Fw/Types/Assert.hpp" +#include + +namespace Os { +//! \brief get implementation file interface for use as delegate +//! +//! Added via linker to ensure that the posix implementation of Os::File is the default. +//! +//! \param aligned_placement_new_memory: memory to placement-new into +//! \return FileInterface pointer result of placement new +//! +FileInterface* FileInterface::getDelegate(U8* aligned_placement_new_memory, const FileInterface* to_copy) { + FW_ASSERT(aligned_placement_new_memory != nullptr); + const Os::Posix::File::PosixFile* copy_me = reinterpret_cast(to_copy); + // Placement-new the file handle into the opaque file-handle storage + static_assert(sizeof(Os::Posix::File::PosixFile) <= sizeof Os::File::m_handle_storage, + "Handle size not large enough"); + static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Posix::File::PosixFile)) == 0, "Handle alignment invalid"); + Os::Posix::File::PosixFile *interface = nullptr; + if (to_copy == nullptr) { + interface = new(aligned_placement_new_memory) Os::Posix::File::PosixFile; + } else { + interface = new(aligned_placement_new_memory) Os::Posix::File::PosixFile(*copy_me); + } + FW_ASSERT(interface != nullptr); + return interface; +} +} diff --git a/Os/Posix/File.cpp b/Os/Posix/File.cpp new file mode 100644 index 0000000000..b78ab80676 --- /dev/null +++ b/Os/Posix/File.cpp @@ -0,0 +1,274 @@ +// ====================================================================== +// \title Os/Posix/File.cpp +// \brief posix implementation for Os::File +// ====================================================================== +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Os { +namespace Posix { +namespace File { + +// Sets up the default file permission as user read + user write +// Some posix systems (e.g. Darwin) use the older S_IREAD and S_IWRITE flags while other systems (e.g. Linux) use the +// newer S_IRUSR and S_IWUSR flags, and some don't support these flags at all. Hence, we look if flags are defined then +// set USER_FLAGS to be the set of flags supported or 0 in the case neither is defined. +#if defined(S_IREAD) && defined(S_IWRITE) +#define USER_FLAGS (S_IREAD | S_IWRITE) +#elif defined(S_IRUSR) && defined(S_IWUSR) +#define USER_FLAGS (S_IRUSR | S_IWUSR) +#else +#define USER_FLAGS (0) +#endif + +// Ensure size of FwSizeType is large enough to fit eh necessary range +static_assert(sizeof(FwSignedSizeType) >= sizeof(off_t), "FwSizeType is not large enough to store values of type off_t"); +static_assert(sizeof(FwSignedSizeType) >= sizeof(ssize_t), "FwSizeType is not large enough to store values of type ssize_t"); + +// Now check ranges of FwSizeType +static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), + "Maximum value of FwSizeType less than the maximum value of off_t. Configure a larger type."); +static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), + "Maximum value of FwSizeType less than the maximum value of ssize_t. Configure a larger type."); +static_assert(std::numeric_limits::min() <= std::numeric_limits::min(), + "Minimum value of FwSizeType larger than the minimum value of off_t. Configure a larger type."); +static_assert(std::numeric_limits::min() <= std::numeric_limits::min(), + "Minimum value of FwSizeType larger than the minimum value of ssize_t. Configure a larger type."); + +//!\brief default copy constructor +PosixFile::PosixFile(const PosixFile& other) { + // Must properly duplicate the file handle + this->m_handle.m_file_descriptor = ::dup(other.m_handle.m_file_descriptor); +}; + +PosixFile& PosixFile::operator=(const PosixFile& other) { + if (this != &other) { + this->m_handle.m_file_descriptor = ::dup(other.m_handle.m_file_descriptor); + } + return *this; +} + +PosixFile::Status PosixFile::open(const char* filepath, PosixFile::Mode requested_mode, PosixFile::OverwriteType overwrite) { + PlatformIntType mode_flags = 0; + Status status = OP_OK; + switch (requested_mode) { + case OPEN_READ: + mode_flags = O_RDONLY; + break; + case OPEN_WRITE: + mode_flags = O_WRONLY | O_CREAT; + break; + case OPEN_SYNC_WRITE: + mode_flags = O_WRONLY | O_CREAT | O_SYNC; + break; + case OPEN_CREATE: + mode_flags = O_WRONLY | O_CREAT | O_TRUNC | ((overwrite == PosixFile::OverwriteType::OVERWRITE) ? 0 : O_EXCL); + break; + case OPEN_APPEND: + mode_flags = O_WRONLY | O_CREAT | O_APPEND; + break; + default: + FW_ASSERT(0, requested_mode); + break; + } + PlatformIntType descriptor = ::open(filepath, mode_flags, USER_FLAGS); + if (PosixFileHandle::INVALID_FILE_DESCRIPTOR == descriptor) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } + this->m_handle.m_file_descriptor = descriptor; + return status; +} + +void PosixFile::close() { + // Only close file handles that are not open + if (PosixFileHandle::INVALID_FILE_DESCRIPTOR != this->m_handle.m_file_descriptor) { + (void)::close(this->m_handle.m_file_descriptor); + this->m_handle.m_file_descriptor = PosixFileHandle::INVALID_FILE_DESCRIPTOR; + } +} + +PosixFile::Status PosixFile::size(FwSignedSizeType& size_result) { + FwSignedSizeType current_position = 0; + Status status = this->position(current_position); + size_result = 0; + if (Os::File::Status::OP_OK == status) { + // Seek to the end of the file to determine size + off_t end_of_file = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_END); + if (PosixFileHandle::ERROR_RETURN_VALUE == end_of_file) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } else { + // Return the file pointer back to the original position + off_t original = ::lseek(this->m_handle.m_file_descriptor, current_position, SEEK_SET); + if ((PosixFileHandle::ERROR_RETURN_VALUE == original) || (current_position != original)) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } + } + size_result = end_of_file; + } + return status; +} + +PosixFile::Status PosixFile::position(FwSignedSizeType &position_result) { + Status status = OP_OK; + position_result = 0; + off_t actual = ::lseek(this->m_handle.m_file_descriptor, 0, SEEK_CUR); + if (PosixFileHandle::ERROR_RETURN_VALUE == actual) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } + position_result = static_cast(actual); + return status; +} + +PosixFile::Status PosixFile::preallocate(FwSignedSizeType offset, FwSignedSizeType length) { + PosixFile::Status status = Os::File::Status::NOT_SUPPORTED; + // posix_fallocate is only available with the posix C-API post version 200112L, however; it is not guaranteed that + // this call is properly implemented. This code starts with a status of "NOT_SUPPORTED". When the standard is met + // an attempt will be made to called posix_fallocate, and should that still return NOT_SUPPORTED then fallback + // code is engaged to synthesize this behavior. +#if _POSIX_C_SOURCE >= 200112L + PlatformIntType errno_status = ::posix_fallocate(this->m_handle.m_file_descriptor, offset, length); + status = Os::Posix::errno_to_file_status(errno_status); +#endif + // When the operation is not supported or posix-API is not sufficient, fallback to a slower algorithm + if (Os::File::Status::NOT_SUPPORTED == status) { + // Calculate size + FwSignedSizeType file_size = 0; + status = this->size(file_size); + if (Os::File::Status::OP_OK == status) { + // Calculate current position + FwSignedSizeType file_position = 0; + status = this->position(file_position); + if (Os::File::Status::OP_OK == status) { + // Check for integer overflow + if ((std::numeric_limits::max() - offset - length) < 0) { + status = PosixFile::NO_SPACE; + } else if (file_size < (offset + length)) { + const FwSignedSizeType write_length = (offset + length) - file_size; + status = this->seek(file_size, PosixFile::SeekType::ABSOLUTE); + if (Os::File::Status::OP_OK == status) { + // Fill in zeros past size of file to ensure compatibility with fallocate + for (FwSignedSizeType i = 0; i < write_length; i++) { + FwSignedSizeType write_size = 1; + status = this->write(reinterpret_cast("\0"), write_size, PosixFile::WaitType::NO_WAIT); + if (Status::OP_OK != status || write_size != 1) { + break; + } + } + // Return to original position + if (Os::File::Status::OP_OK == status) { + status = this->seek(file_position, PosixFile::SeekType::ABSOLUTE); + } + } + } + } + } + } + return status; +} + +PosixFile::Status PosixFile::seek(FwSignedSizeType offset, PosixFile::SeekType seekType) { + Status status = OP_OK; + off_t actual = ::lseek(this->m_handle.m_file_descriptor, offset, (seekType == SeekType::ABSOLUTE) ? SEEK_SET : SEEK_CUR); + PlatformIntType errno_store = errno; + if (actual == PosixFileHandle::ERROR_RETURN_VALUE) { + status = Os::Posix::errno_to_file_status(errno_store); + } else if ((seekType == SeekType::ABSOLUTE) && (actual != offset)) { + status = Os::File::Status::OTHER_ERROR; + } + return status; +} + +PosixFile::Status PosixFile::flush() { + PosixFile::Status status = OP_OK; + if (PosixFileHandle::ERROR_RETURN_VALUE == ::fsync(this->m_handle.m_file_descriptor)) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } + return status; +} + +PosixFile::Status PosixFile::read(U8* buffer, FwSignedSizeType &size, PosixFile::WaitType wait) { + Status status = OP_OK; + FwSignedSizeType accumulated = 0; + // Loop up to 2 times for each by, bounded to prevent overflow + const FwSignedSizeType maximum = (size > (std::numeric_limits::max()/2)) ? std::numeric_limits::max() : size * 2; + + for (FwSignedSizeType i = 0; i < maximum && accumulated < size; i++) { + // char* for some posix implementations + ssize_t read_size = ::read(this->m_handle.m_file_descriptor, reinterpret_cast(&buffer[accumulated]), size - accumulated); + // Non-interrupt error + if (PosixFileHandle::ERROR_RETURN_VALUE == read_size) { + PlatformIntType errno_store = errno; + // Interrupted w/o read, try again + if (EINTR != errno_store) { + continue; + } + status = Os::Posix::errno_to_file_status(errno_store); + break; + } + // End-of-file + else if (read_size == 0) { + break; + } + accumulated += read_size; + // Stop looping when we had a good read and are not waiting + if (not wait) { + break; + } + } + size = accumulated; + return status; +} + +PosixFile::Status PosixFile::write(const U8* buffer, FwSignedSizeType &size, PosixFile::WaitType wait) { + Status status = OP_OK; + FwSignedSizeType accumulated = 0; + // Loop up to 2 times for each by, bounded to prevent overflow + const FwSignedSizeType maximum = (size > (std::numeric_limits::max()/2)) ? std::numeric_limits::max() : size * 2; + + for (FwSignedSizeType i = 0; i < maximum && accumulated < size; i++) { + // char* for some posix implementations + ssize_t write_size = ::write(this->m_handle.m_file_descriptor, reinterpret_cast(&buffer[accumulated]), size - accumulated); + // Non-interrupt error + if (PosixFileHandle::ERROR_RETURN_VALUE == write_size) { + PlatformIntType errno_store = errno; + // Interrupted w/o read, try again + if (EINTR != errno_store) { + continue; + } + status = Os::Posix::errno_to_file_status(errno_store); + break; + } + accumulated += write_size; + } + size = accumulated; + // When waiting, sync to disk + if (wait) { + PlatformIntType fsync_return = ::fsync(this->m_handle.m_file_descriptor); + if (PosixFileHandle::ERROR_RETURN_VALUE == fsync_return) { + PlatformIntType errno_store = errno; + status = Os::Posix::errno_to_file_status(errno_store); + } + } + return status; +} + +FileHandle* PosixFile::getHandle() { + return &this->m_handle; +} + +} // namespace File +} // namespace Posix +} // namespace Os + diff --git a/Os/Posix/File.hpp b/Os/Posix/File.hpp new file mode 100644 index 0000000000..b4229bf8bb --- /dev/null +++ b/Os/Posix/File.hpp @@ -0,0 +1,182 @@ +// ====================================================================== +// \title Os/Posix/File.hpp +// \brief posix implementation for Os::File, header and test definitions +// ====================================================================== +#include +#ifndef OS_POSIX_FILE_HPP +#define OS_POSIX_FILE_HPP + +namespace Os { +namespace Posix { +namespace File { + +//! FileHandle class definition for posix implementations. +//! +struct PosixFileHandle : public FileHandle { + static constexpr PlatformIntType INVALID_FILE_DESCRIPTOR = -1; + static constexpr PlatformIntType ERROR_RETURN_VALUE = -1; + + //! Posix file descriptor + PlatformIntType m_file_descriptor = INVALID_FILE_DESCRIPTOR; +}; + +//! \brief posix implementation of Os::File +//! +//! Posix implementation of `FileInterface` for use as a delegate class handling posix file operations. Posix files use +//! standard `open`, `read`, and `write` posix calls. The handle is represented as a `PosixFileHandle` which wraps a +//! single `int` type file descriptor used in those API calls. +//! +class PosixFile : public FileInterface { + public: + //! \brief constructor + //! + PosixFile() = default; + + //! \brief copy constructor + PosixFile(const PosixFile& other); + + //! \brief assignment operator that copies the internal representation + PosixFile& operator=(const PosixFile& other); + + //! \brief destructor + //! + ~PosixFile() override = default; + + // ------------------------------------ + // Functions overrides + // ------------------------------------ + + //! \brief open file with supplied path and mode + //! + //! Open the file passed in with the given mode. If overwrite is set to OVERWRITE, then opening files in + //! OPEN_CREATE mode will clobber existing files. Set overwrite to NO_OVERWRITE to preserve existing files. + //! The status of the open request is returned from the function call. Delegates to the chosen + //! implementation's `open` function. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! It is invalid to supply `overwrite` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \param overwrite: overwrite existing file on create + //! \return: status of the open + //! + Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override; + + //! \brief close the file, if not opened then do nothing + //! + //! Closes the file, if open. Otherwise this function does nothing. Delegates to the chosen implementation's + //! `closeInternal` function. `mode` is set to `OPEN_NO_MODE`. + //! + void close() override; + + //! \brief get size of currently open file + //! + //! Get the size of the currently open file and fill the size parameter. Return status of the operation. + //! \param size: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status size(FwSignedSizeType &size_result) override; + + //! \brief get file pointer position of the currently open file + //! + //! Get the current position of the read/write pointer of the open file. + //! \param position: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status position(FwSignedSizeType &position_result) override; + + //! \brief pre-allocate file storage + //! + //! Pre-allocates file storage with at least `length` storage starting at `offset`. No-op on implementations + //! that cannot pre-allocate. + //! + //! It is invalid to pass a negative `offset`. + //! It is invalid to pass a negative `length`. + //! + //! \param offset: offset into file + //! \param length: length after offset to preallocate + //! \return OP_OK on success otherwise error status + //! + Status preallocate(FwSignedSizeType offset, FwSignedSizeType length) override; + + //! \brief seek the file pointer to the given offset + //! + //! Seek the file pointer to the given `offset`. If `seekType` is set to `ABSOLUTE` then the offset is calculated + //! from the start of the file, and if it is set to `CURRENT` it is calculated from the current position. + //! + //! \param offset: offset to seek to + //! \param seekType: `ABSOLUTE` for seeking from beginning of file, `CURRENT` to use current position. + //! \return OP_OK on success otherwise error status + //! + Status seek(FwSignedSizeType offset, SeekType seekType) override; + + //! \brief flush file contents to storage + //! + //! Flushes the file contents to storage (i.e. out of the OS cache to disk). Does nothing in implementations + //! that do not support flushing. + //! + //! \return OP_OK on success otherwise error status + //! + Status flush() override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Read data from this file up to the `size` and store it in `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been read successfully read or the end of the file has been + //! reached. When `wait` is set to `NO_WAIT` it will return whatever data is currently available. + //! + //! `size` will be updated to the count of bytes actually read. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status read(U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Write data to this file up to the `size` from the `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been written successfully to disk. When `wait` is set to + //! `NO_WAIT` it will return once the data is sent to the OS. + //! + //! `size` will be updated to the count of bytes actually written. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data to write to disk, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status write(const U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief returns the raw file handle + //! + //! Gets the raw file handle from the implementation. Note: users must include the implementation specific + //! header to make any real use of this handle. Otherwise it//!must* be passed as an opaque type. + //! + //! \return raw file handle + //! + FileHandle *getHandle() override; + + + private: + //! File handle for PosixFile + PosixFileHandle m_handle; +}; +} // namespace File +} // namespace Posix +} // namespace Os + +#endif // OS_POSIX_FILE_HPP diff --git a/Os/Posix/errno.cpp b/Os/Posix/errno.cpp new file mode 100644 index 0000000000..6825168f25 --- /dev/null +++ b/Os/Posix/errno.cpp @@ -0,0 +1,52 @@ +// ====================================================================== +// \title Os/Posix/errno.cpp +// \brief implementation for posix errno conversion +// ====================================================================== +#include +#include "Os/Posix/errno.hpp" + +namespace Os { +namespace Posix { + +File::Status errno_to_file_status(PlatformIntType errno_input) { + File::Status status = File::Status::OP_OK; + switch (errno_input) { + case 0: + status = File::Status::OP_OK; + break; + // Fallthrough intended + case ENOSPC: + case EFBIG: + status = File::Status::NO_SPACE; + break; + case ENOENT: + status = File::Status::DOESNT_EXIST; + break; + // Fallthrough intended + case EPERM: + case EACCES: + status = File::Status::NO_PERMISSION; + break; + case EEXIST: + status = File::Status::FILE_EXISTS; + break; + case EBADF: + status = File::Status::NOT_OPENED; + break; + // Fallthrough intended + case ENOSYS: + case EOPNOTSUPP: + status = File::Status::NOT_SUPPORTED; + break; + case EINVAL: + status = File::Status::INVALID_ARGUMENT; + break; + default: + status = File::Status::OTHER_ERROR; + break; + } + return status; +} + +} +} diff --git a/Os/Posix/errno.hpp b/Os/Posix/errno.hpp new file mode 100644 index 0000000000..dd89888c32 --- /dev/null +++ b/Os/Posix/errno.hpp @@ -0,0 +1,20 @@ +// ====================================================================== +// \title Os/Posix/errno.hpp +// \brief header for posix errno conversion +// ====================================================================== +#include "Os/File.hpp" +#ifndef OS_POSIX_ERRNO_HPP +#define OS_POSIX_ERRNO_HPP + +namespace Os { +namespace Posix { + +//! Convert an errno representation of an error to the Os::File::Status representation. +//! \param errno: errno representation of the error +//! \return: Os::File::Status representation of the error +//! +Os::File::Status errno_to_file_status(PlatformIntType errno_input); + +} +} +#endif diff --git a/Os/Posix/test/ut/PosixFileTests.cpp b/Os/Posix/test/ut/PosixFileTests.cpp new file mode 100644 index 0000000000..d75c5bc40f --- /dev/null +++ b/Os/Posix/test/ut/PosixFileTests.cpp @@ -0,0 +1,144 @@ +// ====================================================================== +// \title Os/Posix/test/ut/PosixFileTests.cpp +// \brief tests for posix implementation for Os::File +// ====================================================================== +#include +#include +#include +#include "Os/File.hpp" +#include "Os/Posix/File.hpp" +#include "Os/test/ut/file/CommonTests.hpp" +#include "STest/Pick/Pick.hpp" +#include +#include +namespace Os { +namespace Test { +namespace File { + +std::vector > FILES; + +static const U32 MAX_FILES = 100; +static const char BASE_PATH[] = "/tmp/fprime"; +static const char TEST_FILE[] = "fprime-os-file-test"; +//! Check if we can use the file. F_OK file exists, R_OK, W_OK are read and write. +//! \return true if it exists, false otherwise. +//! +bool check_permissions(const char* path, int permission) { + return ::access(path, permission) == 0; +} + +//! Get a filename, randomly if random is true, otherwise use a basic filename. +//! \param random: true if filename should be random, false if predictable +//! \return: filename to use for testing +//! +std::shared_ptr get_test_filename(bool random) { + const char* filename = TEST_FILE; + char full_buffer[PATH_MAX]; + char buffer[PATH_MAX - sizeof(BASE_PATH)]; + // When random, select random characters + if (random) { + filename = buffer; + size_t i = 0; + for (i = 0; i < STest::Pick::lowerUpper(1, (sizeof buffer) - 1); i++) { + char selected_character = static_cast(STest::Pick::lowerUpper(32, 126)); + selected_character = + (selected_character == '/') ? static_cast(selected_character + 1) : selected_character; + buffer[i] = selected_character; + } + buffer[i] = 0; // Terminate random string + } + (void) snprintf(full_buffer, PATH_MAX, "%s/%s", BASE_PATH, filename); + // Create a shared pointer wrapping our filename buffer + std::shared_ptr pointer(new std::string(full_buffer), std::default_delete()); + return pointer; +} + +//! Clean-up the files created during this test. +//! +void cleanup(int signal) { + // Ensure the test files are removed only when the test was run + for (const auto& val : FILES) { + if (check_permissions(val->c_str(), F_OK)) { + ::unlink(val->c_str()); + } + } + FILES.clear(); +} + +//! Set up for the test ensures that the test can run at all +//! +void setUp(bool requires_io) { + std::shared_ptr non_random_filename = get_test_filename(false); + int result = mkdir(BASE_PATH, 0777); + // Check that we could make the directory for test files + if (result != 0 && errno != EEXIST) { + GTEST_SKIP() << "Cannot make directory for test files: " << strerror(errno); + } + // IO required and test file exists then skip + else if (check_permissions(non_random_filename->c_str(), F_OK)) { + GTEST_SKIP() << "Test file exists: " << non_random_filename->c_str(); + } + // IO required and cannot read/write to BASE_PATH then skip + else if (requires_io && not check_permissions(BASE_PATH, R_OK & W_OK)) { + GTEST_SKIP() << "Cannot read/write in directory: " << BASE_PATH; + } + int signals[] = {SIGQUIT, SIGABRT, SIGTERM, SIGINT, SIGHUP}; + for (unsigned long i = 0; i < FW_NUM_ARRAY_ELEMENTS(signals); i++) { + // Could not register signal handler + if (signal(SIGQUIT, cleanup) == SIG_ERR) { + GTEST_SKIP() << "Cannot register signal handler for cleanup"; + } + } +} + +//! Tear down for the tests cleans up the test file used +//! +void tearDown() { + cleanup(0); +} + +class PosixTester : public Tester { + //! Check if the test file exists. + //! \return true if it exists, false otherwise. + //! + bool exists(const std::string& filename) const override { + bool exits = check_permissions(filename.c_str(), F_OK); + return exits; + } + + //! Get a filename, randomly if random is true, otherwise use a basic filename. + //! \param random: true if filename should be random, false if predictable + //! \return: filename to use for testing + //! + std::shared_ptr get_filename(bool random) const override { + U32 pick = STest::Pick::lowerUpper(0, MAX_FILES); + if (random && pick < FILES.size()) { + return FILES[pick]; + } + std::shared_ptr filename = get_test_filename(random); + FILES.push_back(filename); + return filename; + } + + //! Posix tester is fully functional + //! \return true + //! + bool functional() const override{ + return true; + } + +}; + +std::unique_ptr get_tester_implementation() { + return std::unique_ptr(new Os::Test::File::PosixTester()); +} + +} // namespace File +} // namespace Test +} // namespace Os + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Os/Stub/CMakeLists.txt b/Os/Stub/CMakeLists.txt new file mode 100644 index 0000000000..7f62b620c6 --- /dev/null +++ b/Os/Stub/CMakeLists.txt @@ -0,0 +1,40 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +#### +add_custom_target("${FPRIME_CURRENT_MODULE}") +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/File.cpp" + "${CMAKE_CURRENT_LIST_DIR}/DefaultFile.cpp" +) +register_fprime_module(Os_File_Stub) +register_fprime_implementation(Os/File Os_File_Stub) + +# Remainder of file is specific to UTs +if (NOT BUILD_TESTING) + return() +endif () + +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/File.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/DefaultFile.cpp" +) +register_fprime_module(Os_File_Test_Stub) +register_fprime_implementation(Os/File Os_File_Test_Stub) + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/StubFileTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/CommonTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/FileRules.cpp" +) +set(UT_MOD_DEPS + Os + Os_Models + Os_Test_File_SyntheticFileSystem + STest +) +choose_fprime_implementation(Os/File Os_File_Test_Stub) +register_fprime_ut(StubFileTest) diff --git a/Os/Stub/DefaultFile.cpp b/Os/Stub/DefaultFile.cpp new file mode 100644 index 0000000000..d5285656f2 --- /dev/null +++ b/Os/Stub/DefaultFile.cpp @@ -0,0 +1,31 @@ +// ====================================================================== +// \title Os/Stub/DefaultFile.cpp +// \brief sets default Os::File to no-op stub implementation via linker +// ====================================================================== +#include "Os/Stub/File.hpp" +#include "Fw/Types/Assert.hpp" +#include +namespace Os { +//! \brief get implementation file interface for use as delegate +//! +//! Added via linker to ensure that the no-op stub implementation of Os::File is the default. +//! +//! \param aligned_placement_new_memory: memory to placement-new into +//! \return FileInterface pointer result of placement new +//! +FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) { + FW_ASSERT(aligned_placement_new_memory != nullptr); + const Os::Stub::File::StubFile* copy_me = reinterpret_cast(to_copy); + // Placement-new the file handle into the opaque file-handle storage + static_assert(sizeof(Os::Stub::File::StubFile) <= sizeof Os::File::m_handle_storage, "Handle size not large enough"); + static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Stub::File::StubFile)) == 0, "Handle alignment invalid"); + Os::Stub::File::StubFile *interface = nullptr; + if (to_copy == nullptr) { + interface = new(aligned_placement_new_memory) Os::Stub::File::StubFile; + } else { + interface = new(aligned_placement_new_memory) Os::Stub::File::StubFile(*copy_me); + } + FW_ASSERT(interface != nullptr); + return interface; +} +} diff --git a/Os/Stub/File.cpp b/Os/Stub/File.cpp new file mode 100644 index 0000000000..d8f8a1907d --- /dev/null +++ b/Os/Stub/File.cpp @@ -0,0 +1,59 @@ +// ====================================================================== +// \title Os/Stub/File.cpp +// \brief stub implementation for Os::File +// ====================================================================== +#include "Os/Stub/File.hpp" + +namespace Os { +namespace Stub { +namespace File { + + StubFile::Status StubFile::open(const char *filepath, StubFile::Mode open_mode, OverwriteType overwrite) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + void StubFile::close() {} + + StubFile::Status StubFile::size(FwSignedSizeType &size_result) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::position(FwSignedSizeType &position_result) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::preallocate(FwSignedSizeType offset, FwSignedSizeType length) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::seek(FwSignedSizeType offset, SeekType seekType) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::flush() { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::read(U8 *buffer, FwSignedSizeType &size, StubFile::WaitType wait) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + StubFile::Status StubFile::write(const U8 *buffer, FwSignedSizeType &size, StubFile::WaitType wait) { + Status status = Status::NOT_SUPPORTED; + return status; + } + + FileHandle* StubFile::getHandle() { + return &this->m_handle; + } + +} // namespace File +} // namespace Stub +} // namespace Os diff --git a/Os/Stub/File.hpp b/Os/Stub/File.hpp new file mode 100644 index 0000000000..749a072486 --- /dev/null +++ b/Os/Stub/File.hpp @@ -0,0 +1,149 @@ +// ====================================================================== +// \title Os/Stub/File.hpp +// \brief stub file definitions for Os::File +// ====================================================================== +#include "Os/File.hpp" + +#ifndef OS_STUB_FILE_HPP +#define OS_STUB_FILE_HPP +namespace Os { +namespace Stub { +namespace File { + +struct StubFileHandle : public FileHandle {}; + +//! \brief stub implementation of Os::File +//! +//! Stub implementation of `FileInterface` for use as a delegate class handling error-only file operations. +//! +class StubFile : public FileInterface { + public: + //! \brief constructor + //! + StubFile() = default; + + //! \brief destructor + //! + ~StubFile() override = default; + + // ------------------------------------ + // Functions overrides + // ------------------------------------ + + //! \brief open file with supplied path and mode + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! It is invalid to supply `overwrite` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \param overwrite: overwrite existing file on create + //! \return: NOT_IMPLEMENTED + //! + Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override; + + //! \brief close the file, if not opened then do nothing + //! + //! This implementation does nothing. + //! + void close() override; + + //! \brief get size of currently open file + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! \param size: output parameter for size. + //! \return NOT_IMPLEMENTED + //! + Status size(FwSignedSizeType &size_result) override; + + //! \brief get file pointer position of the currently open file + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! \param position: output parameter for size. + //! \return NOT_IMPLEMENTED + //! + Status position(FwSignedSizeType &position_result) override; + + //! \brief pre-allocate file storage + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! It is invalid to pass a negative `offset`. + //! It is invalid to pass a negative `length`. + //! + //! \param offset: offset into file + //! \param length: length after offset to preallocate + //! \return NOT_IMPLEMENTED + //! + Status preallocate(FwSignedSizeType offset, FwSignedSizeType length) override; + + //! \brief seek the file pointer to the given offset + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! \param offset: offset to seek to + //! \param seekType: `ABSOLUTE` for seeking from beginning of file, `CURRENT` to use current position. + //! \return NOT_IMPLEMENTED + //! + Status seek(FwSignedSizeType offset, SeekType seekType) override; + + //! \brief flush file contents to storage + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! \return NOT_IMPLEMENTED + //! + Status flush() override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data, `NO_WAIT` to return what is currently available + //! \return NOT_IMPLEMENTED + //! + Status read(U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! This implementation does nothing but return NOT_IMPLEMENTED. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data to write to disk, `NO_WAIT` to return what is currently available + //! \return NOT_IMPLEMENTED + //! + Status write(const U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief returns the raw file handle + //! + //! Gets the raw file handle from the implementation. Note: users must include the implementation specific + //! header to make any real use of this handle. Otherwise it//!must* be passed as an opaque type. + //! + //! \return raw file handle + //! + FileHandle *getHandle() override; + + +private: + //! File handle for PosixFile + StubFileHandle m_handle; +}; + +} // namespace File +} // namespace Stub +} // namespace Os +#endif // OS_STUB_FILE_HPP diff --git a/Os/Stub/test/DefaultFile.cpp b/Os/Stub/test/DefaultFile.cpp new file mode 100644 index 0000000000..e40651c4e5 --- /dev/null +++ b/Os/Stub/test/DefaultFile.cpp @@ -0,0 +1,32 @@ +// ====================================================================== +// \title Os/Stub/test/DefaultFile.cpp +// \brief sets default Os::File to test stub implementation via linker +// ====================================================================== +#include "Os/Stub/test/File.hpp" +#include "Fw/Types/Assert.hpp" +#include +namespace Os { +//! \brief get implementation file interface for use as delegate +//! +//! Added via linker to ensure that the tracking test stub implementation of Os::File is the default. +//! +//! \param aligned_placement_new_memory: memory to placement-new into +//! \return FileInterface pointer result of placement new +//! +FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) { + FW_ASSERT(aligned_placement_new_memory != nullptr); + const Os::Stub::File::Test::TestFile* copy_me = reinterpret_cast(to_copy); + // Placement-new the file handle into the opaque file-handle storage + static_assert(sizeof(Os::Stub::File::Test::TestFile) <= sizeof Os::File::m_handle_storage, + "Handle size not large enough"); + static_assert((FW_HANDLE_ALIGNMENT % alignof(Os::Stub::File::Test::TestFile)) == 0, "Handle alignment invalid"); + Os::Stub::File::Test::TestFile *interface = nullptr; + if (to_copy == nullptr) { + interface = new(aligned_placement_new_memory) Os::Stub::File::Test::TestFile; + } else { + interface = new(aligned_placement_new_memory) Os::Stub::File::Test::TestFile(*copy_me); + } + FW_ASSERT(interface != nullptr); + return interface; +} +} diff --git a/Os/Stub/test/File.cpp b/Os/Stub/test/File.cpp new file mode 100644 index 0000000000..08c515dec5 --- /dev/null +++ b/Os/Stub/test/File.cpp @@ -0,0 +1,132 @@ +#include "Os/Stub/test/File.hpp" +#include "Os/File.hpp" +#include +namespace Os { +namespace Stub { +namespace File { +namespace Test { + +StaticData StaticData::data; + +void StaticData::setNextStatus(Os::File::Status status) { + StaticData::data.nextStatus = status; +} + +void StaticData::setSizeResult(FwSignedSizeType size) { + StaticData::data.sizeResult = size; +} + +void StaticData::setPositionResult(FwSignedSizeType position) { + StaticData::data.positionResult = position; +} + +void StaticData::setReadResult(U8 *buffer, FwSignedSizeType size) { + StaticData::data.readResult = buffer; + StaticData::data.readResultSize = size; +} + +void StaticData::setReadSize(FwSignedSizeType size) { + StaticData::data.readSizeResult = size; +} + +void StaticData::setWriteResult(U8 *buffer, FwSignedSizeType size) { + StaticData::data.writeResult = buffer; + StaticData::data.writeResultSize = size; +} + +void StaticData::setWriteSize(FwSignedSizeType size) { + StaticData::data.writeSizeResult = size; +} + +TestFile::TestFile() { + StaticData::data.lastCalled = StaticData::CONSTRUCT_FN; +} + +TestFile::~TestFile() { + StaticData::data.lastCalled = StaticData::DESTRUCT_FN; +} + +FileInterface::Status TestFile::open(const char *filepath, Mode open_mode, OverwriteType overwrite) { + StaticData::data.openPath = filepath; + StaticData::data.openMode = open_mode; + StaticData::data.openOverwrite = overwrite; + StaticData::data.lastCalled = StaticData::OPEN_FN; + StaticData::data.pointer = 0; + return StaticData::data.nextStatus; +} + +void TestFile::close() { + StaticData::data.lastCalled = StaticData::CLOSE_FN; +} + +FileInterface::Status TestFile::size(FwSignedSizeType& size_result) { + StaticData::data.lastCalled = StaticData::SIZE_FN; + size_result = StaticData::data.sizeResult; + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::position(FwSignedSizeType& position_result) { + StaticData::data.lastCalled = StaticData::POSITION_FN; + position_result = StaticData::data.positionResult; + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::preallocate(FwSignedSizeType offset, FwSignedSizeType length) { + StaticData::data.preallocateOffset = offset; + StaticData::data.preallocateLength = length; + StaticData::data.lastCalled = StaticData::PREALLOCATE_FN; + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::seek(FwSignedSizeType offset, SeekType seekType) { + StaticData::data.seekOffset = offset; + StaticData::data.seekType = seekType; + StaticData::data.lastCalled = StaticData::SEEK_FN; + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::flush() { + StaticData::data.lastCalled = StaticData::FLUSH_FN; + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::read(U8 *buffer, FwSignedSizeType &size, WaitType wait) { + StaticData::data.readBuffer = buffer; + StaticData::data.readSize = size; + StaticData::data.readWait = wait; + StaticData::data.lastCalled = StaticData::READ_FN; + // Copy read data if set + if (nullptr != StaticData::data.readResult) { + size = FW_MIN(size, StaticData::data.readResultSize - StaticData::data.pointer); + (void) ::memcpy(buffer, StaticData::data.readResult + StaticData::data.pointer, size); + StaticData::data.pointer += size; + } else { + size = StaticData::data.readSizeResult; + } + return StaticData::data.nextStatus; +} + +FileInterface::Status TestFile::write(const U8* buffer, FwSignedSizeType &size, WaitType wait) { + StaticData::data.writeBuffer = buffer; + StaticData::data.writeSize = size; + StaticData::data.writeWait = wait; + StaticData::data.lastCalled = StaticData::WRITE_FN; + // Copy read data if set + if (nullptr != StaticData::data.writeResult) { + size = FW_MIN(size, StaticData::data.writeResultSize - StaticData::data.pointer); + (void) ::memcpy(StaticData::data.writeResult + StaticData::data.pointer, buffer, size); + StaticData::data.pointer += size; + } else { + size = StaticData::data.writeSizeResult; + } + return StaticData::data.nextStatus; +} + +FileHandle* TestFile::getHandle() { + return &this->m_handle; +} + +} // namespace Test +} // namespace File +} // namespace Stub +} // namespace Os diff --git a/Os/Stub/test/File.hpp b/Os/Stub/test/File.hpp new file mode 100644 index 0000000000..051bbc1d9d --- /dev/null +++ b/Os/Stub/test/File.hpp @@ -0,0 +1,264 @@ +#include "Os/File.hpp" +#include + +#ifndef OS_STUB_FILE_TEST_HPP +#define OS_STUB_FILE_TEST_HPP +namespace Os { +namespace Stub { +namespace File { +namespace Test { + +//! Data that supports the stubbed File implementation. +//!/ +struct StaticData { + //! Enumeration of last function called + //! + enum LastFn { + NONE_FN, + CONSTRUCT_FN, + DESTRUCT_FN, + OPEN_FN, + CLOSE_FN, + SIZE_FN, + POSITION_FN, + PREALLOCATE_FN, + SEEK_FN, + FLUSH_FN, + READ_FN, + WRITE_FN + }; + + //! Last function called + LastFn lastCalled = NONE_FN; + //! Path of last open call + const char *openPath = nullptr; + //! Mode of last open call + Os::File::Mode openMode = Os::File::MAX_OPEN_MODE; + //! Overwrite of last open call + Os::File::OverwriteType openOverwrite = Os::File::OverwriteType::NO_OVERWRITE; + //! Offset of last preallocate call + FwSignedSizeType preallocateOffset = -1; + //! Length of last preallocate call + FwSignedSizeType preallocateLength = -1; + //! Offset of last seek call + FwSignedSizeType seekOffset = -1; + //! Absolute of last seek call + Os::File::SeekType seekType = Os::File::SeekType::ABSOLUTE; + //! Buffer of last read call + U8 *readBuffer = nullptr; + //! Size of last read call + FwSignedSizeType readSize = -1; + //! Wait of last read call + Os::File::WaitType readWait = Os::File::WaitType::NO_WAIT; + //! Buffer of last write call + const void *writeBuffer = nullptr; + //! Size of last write call + FwSignedSizeType writeSize = -1; + //! Wait of last write call + Os::File::WaitType writeWait = Os::File::WaitType::NO_WAIT; + + //! File pointer + FwSignedSizeType pointer = 0; + + //! Next status to be returned + Os::File::Status nextStatus = Os::File::Status::OTHER_ERROR; + //! Return of next size call + FwSignedSizeType sizeResult = -1; + //! Return of next position call + FwSignedSizeType positionResult = 0; + //! Result of next read call + U8 *readResult = nullptr; + //! Size result of next read data + FwSignedSizeType readResultSize = -1; + //! Size result of next read call + FwSignedSizeType readSizeResult = -1; + //! Result holding buffer of next write call + U8 *writeResult = nullptr; + //! Size result of next write data + FwSignedSizeType writeResultSize = -1; + //! Size result of next read call + FwSignedSizeType writeSizeResult = -1; + + // Singleton data + static StaticData data; + + //! Set next status to return + static void setNextStatus(Os::File::Status status); + + //! Set next size to result + static void setSizeResult(FwSignedSizeType size); + + //! Set next position to result + static void setPositionResult(FwSignedSizeType position); + + //! Set next read result + static void setReadResult(U8 *buffer, FwSignedSizeType size); + + //! Set next read size result + static void setReadSize(FwSignedSizeType size); + + //! Set next write result + static void setWriteResult(U8 *buffer, FwSignedSizeType size); + + //! Set next write size result + static void setWriteSize(FwSignedSizeType size); +}; + +class TestFileHandle : public FileHandle {}; + +//! \brief tracking implementation of Os::File +//! +//! Tracking implementation of `FileInterface` for use as a delegate class handling test file operations. Posix files use +//! standard `open`, `read`, and `write` posix calls. The handle is represented as a `PosixFileHandle` which wraps a +//! single `int` type file descriptor used in those API calls. +//! +class TestFile : public FileInterface { + public: + //! \brief constructor + //! + TestFile(); + + //! \brief destructor + //! + ~TestFile() override; + + // ------------------------------------ + // Functions overrides + // ------------------------------------ + + //! \brief open file with supplied path and mode + //! + //! Open the file passed in with the given mode. If overwrite is set to OVERWRITE, then opening files in + //! OPEN_CREATE mode will clobber existing files. Set overwrite to NO_OVERWRITE to preserve existing files. + //! The status of the open request is returned from the function call. Delegates to the chosen + //! implementation's `open` function. + //! + //! It is invalid to send `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! It is invalid to supply `overwrite` as a non-enumerated value. + //! + //! \param path: c-string of path to open + //! \param mode: file operation mode + //! \param overwrite: overwrite existing file on create + //! \return: status of the open + //! + Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override; + + //! \brief close the file, if not opened then do nothing + //! + //! Closes the file, if open. Otherwise this function does nothing. Delegates to the chosen implementation's + //! `closeInternal` function. `mode` is set to `OPEN_NO_MODE`. + //! + void close() override; + + //! \brief get size of currently open file + //! + //! Get the size of the currently open file and fill the size parameter. Return status of the operation. + //! \param size: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status size(FwSignedSizeType &size_result) override; + + //! \brief get file pointer position of the currently open file + //! + //! Get the current position of the read/write pointer of the open file. + //! \param position: output parameter for size. + //! \return OP_OK on success otherwise error status + //! + Status position(FwSignedSizeType &position_result) override; + + //! \brief pre-allocate file storage + //! + //! Pre-allocates file storage with at least `length` storage starting at `offset`. No-op on implementations + //! that cannot pre-allocate. + //! + //! It is invalid to pass a negative `offset`. + //! It is invalid to pass a negative `length`. + //! + //! \param offset: offset into file + //! \param length: length after offset to preallocate + //! \return OP_OK on success otherwise error status + //! + Status preallocate(FwSignedSizeType offset, FwSignedSizeType length) override; + + //! \brief seek the file pointer to the given offset + //! + //! Seek the file pointer to the given `offset`. If `seekType` is set to `ABSOLUTE` then the offset is calculated + //! from the start of the file, and if it is set to `CURRENT` it is calculated from the current position. + //! + //! \param offset: offset to seek to + //! \param seekType: `ABSOLUTE` for seeking from beginning of file, `CURRENT` to use current position. + //! \return OP_OK on success otherwise error status + //! + Status seek(FwSignedSizeType offset, SeekType seekType) override; + + //! \brief flush file contents to storage + //! + //! Flushes the file contents to storage (i.e. out of the OS cache to disk). Does nothing in implementations + //! that do not support flushing. + //! + //! \return OP_OK on success otherwise error status + //! + Status flush() override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Read data from this file up to the `size` and store it in `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been read successfully read or the end of the file has been + //! reached. When `wait` is set to `NO_WAIT` it will return whatever data is currently available. + //! + //! `size` will be updated to the count of bytes actually read. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status read(U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief read data from this file into supplied buffer bounded by size + //! + //! Write data to this file up to the `size` from the `buffer`. When `wait` is set to `WAIT`, this + //! will block until the requested size has been written successfully to disk. When `wait` is set to + //! `NO_WAIT` it will return once the data is sent to the OS. + //! + //! `size` will be updated to the count of bytes actually written. Status will reflect the success/failure of + //! the read operation. + //! + //! It is invalid to pass `nullptr` to this function call. + //! It is invalid to pass a negative `size`. + //! It is invalid to supply wait as a non-enumerated value. + //! + //! \param buffer: memory location to store data read from file + //! \param size: size of data to read + //! \param wait: `WAIT` to wait for data to write to disk, `NO_WAIT` to return what is currently available + //! \return OP_OK on success otherwise error status + //! + Status write(const U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + //! \brief returns the raw file handle + //! + //! Gets the raw file handle from the implementation. Note: users must include the implementation specific + //! header to make any real use of this handle. Otherwise it//!must* be passed as an opaque type. + //! + //! \return raw file handle + //! + FileHandle *getHandle() override; + + +private: + //! File handle for PosixFile + TestFileHandle m_handle; +}; + + +} // namespace Test +} // namespace File +} // namespace Stub +} // namespace Os +#endif // OS_STUB_FILE_TEST_HPP diff --git a/Os/Stub/test/ut/StubFileTests.cpp b/Os/Stub/test/ut/StubFileTests.cpp new file mode 100644 index 0000000000..8ec6f35744 --- /dev/null +++ b/Os/Stub/test/ut/StubFileTests.cpp @@ -0,0 +1,215 @@ +// ====================================================================== +// \title Os/Stub/test/ut/StubFileTests.cpp +// \brief tests using stub implementation for Os::File interface testing +// ====================================================================== +#include +#include "Os/File.hpp" +#include "Os/Models/Models.hpp" +#include "Os/test/ut/file/CommonTests.hpp" +#include "Os/test/ut/file/RulesHeaders.hpp" +#include "Os/Stub/test/File.hpp" + +namespace Os { +namespace Test { +namespace File { + +//! Set up for the test ensures that the test can run at all +//! +void setUp(bool requires_io) { + if (requires_io) { + GTEST_SKIP() << "Cannot run tests requiring functional i/o"; + } + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setPositionResult(0); +} +std::vector > FILES; +//! Tear down for the tests cleans up the test file used +//! +void tearDown() {} + +class StubsTester : public Tester { + //! Check if the test file exists. + //! \return true if it exists, false otherwise. + //! + bool exists(const std::string &filename) const override { + for (size_t i = 0; i < FILES.size(); i++) { + if (filename == *FILES.at(i)) { + return true; + } + } + return false; + } + + //! Get a filename, randomly if random is true, otherwise use a basic filename. + //! \param random: true if filename should be random, false if predictable + //! \return: filename to use for testing + //! + std::shared_ptr get_filename(bool random) const override { + std::shared_ptr filename = std::shared_ptr(new std::string("DOES-NOT-MATTER"), std::default_delete()); + FILES.push_back(filename); + return filename; + } + + //! Posix tester is fully functional + //! \return true + //! + bool functional() const override { + return false; + } + +}; + +std::unique_ptr get_tester_implementation() { + return std::unique_ptr(new Os::Test::File::StubsTester()); +} + + +} // namespace File +} // namespace Test +} // namespace Os + +// Ensure that Os::File properly routes constructor calls to the `constructInternal` function. +TEST_F(Interface, Construction) { + Os::File file; + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::CONSTRUCT_FN); +} + +// Ensure that Os::File properly routes destructor calls to the `destructInternal` function. +TEST_F(Interface, Destruction) { + delete (new Os::File); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::DESTRUCT_FN); +} + +// Ensure that Os::File properly routes open calls to the `openInternal` function. +TEST_F(Interface, Open) { + const char* path = "/does/not/matter"; + Os::File file; + ASSERT_EQ(file.open(path, Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::Status::OP_OK); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::OPEN_FN); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.openPath, path); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.openMode, Os::File::OPEN_CREATE); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.openOverwrite, Os::File::OverwriteType::OVERWRITE); +} + +// Ensure that Os::File properly routes close calls to the `closeInternal` function. +TEST_F(Interface, Close) { + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + file.close(); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::CLOSE_FN); +} + +// Ensure that Os::File properly routes close calls to the `sizeInternal` function. +TEST_F(Interface, Size) { + FwSignedSizeType sizeResult = -1; + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setSizeResult(30); + ASSERT_EQ(file.size(sizeResult), Os::File::OP_OK); + ASSERT_EQ(sizeResult, 30); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::SIZE_FN); +} + +// Ensure that Os::File properly routes close calls to the `positionInternal` function. +TEST_F(Interface, Position) { + FwSignedSizeType positionResult = -1; + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setPositionResult(50); + ASSERT_EQ(file.position(positionResult), Os::File::OP_OK); + ASSERT_EQ(positionResult, 50); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::POSITION_FN); +} + +// Ensure that Os::File properly routes preallocate calls to the `preallocateInternal` function. +TEST_F(Interface, Preallocate) { + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OTHER_ERROR); + ASSERT_EQ(file.preallocate(0, 0), Os::File::Status::OTHER_ERROR); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::PREALLOCATE_FN); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.preallocateOffset, 0); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.preallocateLength, 0); + +} + +// Ensure that Os::File properly routes seek calls to the `seekInternal` function. +TEST_F(Interface, Seek) { + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OTHER_ERROR); + ASSERT_EQ(file.seek(0, Os::File::SeekType::ABSOLUTE), Os::File::Status::OTHER_ERROR); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::SEEK_FN); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.seekOffset, 0); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.seekType, Os::File::SeekType::ABSOLUTE); +} + +// Ensure that Os::File properly routes flush calls to the `flushInternal` function. +TEST_F(Interface, Flush) { + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OTHER_ERROR); + ASSERT_EQ(file.flush(), Os::File::Status::OTHER_ERROR); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::FLUSH_FN); +} + +// Ensure that Os::File properly routes flush calls to the `flushInternal` function. +TEST_F(Interface, Read) { + U8 buffer[] = {0xab, 0xcd, 0xef}; + FwSignedSizeType size = static_cast(sizeof buffer); + FwSignedSizeType original_size = size; + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_READ, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OTHER_ERROR); + ASSERT_EQ(file.read(buffer, size, Os::File::WaitType::WAIT), Os::File::Status::OTHER_ERROR); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::READ_FN); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.readBuffer, buffer); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.readSize, original_size); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.readWait, Os::File::WaitType::WAIT); +} + +// Ensure that Os::File properly routes statuses returned from the `flushInternal` function back to the caller. +TEST_F(Interface, Write) { + U8 buffer[] = {0xab, 0xcd, 0xef}; + FwSignedSizeType size = static_cast(sizeof buffer); + FwSignedSizeType original_size = size; + Os::File file; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + ASSERT_EQ(file.open("/does/not/matter", Os::File::OPEN_WRITE, Os::File::OverwriteType::OVERWRITE), Os::File::OP_OK); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OTHER_ERROR); + ASSERT_EQ(file.write(buffer, size, Os::File::WaitType::WAIT), Os::File::Status::OTHER_ERROR); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.lastCalled, Os::Stub::File::Test::StaticData::WRITE_FN); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.writeBuffer, buffer); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.writeSize, original_size); + ASSERT_EQ(Os::Stub::File::Test::StaticData::data.writeWait, Os::File::WaitType::WAIT); +} + +// Ensure that FPP shadow enumeration matches +TEST(FppTypes, FileStatusEnum) { + for (FwIndexType i = static_cast(Os::File::Status::OP_OK); i < static_cast(Os::File::Status::MAX_STATUS); i++){ + Os::File::Status status = static_cast(i); + Os::FileStatus fpp_status = static_cast(status); + ASSERT_TRUE(fpp_status.isValid()); + } +} + +// Ensure that FPP shadow enumeration matches +TEST(FppTypes, FileModeEnum) { + for (FwIndexType i = static_cast(Os::File::Mode::OPEN_CREATE); i < static_cast(Os::File::Mode::MAX_OPEN_MODE); i++){ + Os::File::Mode mode = static_cast(i); + Os::FileMode fpp_mode = static_cast(mode); + ASSERT_TRUE(fpp_mode.isValid()); + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Os/Stubs/CMakeLists.txt b/Os/Stubs/CMakeLists.txt deleted file mode 100644 index 2a3ff41f98..0000000000 --- a/Os/Stubs/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -#### -# F prime CMakeLists.txt: -# -# SOURCE_FILES: combined list of source and autocoding files -# MOD_DEPS: (optional) module dependencies -# -#### -set(SOURCE_FILES - "${CMAKE_CURRENT_LIST_DIR}/Linux/FileStub.cpp" -) -set(MOD_DEPS - Fw/Cfg - Fw/Types -) - -register_fprime_module() - -# Sets MODULE_NAME to unique name based on path -get_module_name(${CMAKE_CURRENT_LIST_DIR}) - -# Exclude test module from all build -set_target_properties( - ${MODULE_NAME} - PROPERTIES - EXCLUDE_FROM_ALL TRUE -) -# Sets MODULE_NAME to unique name based on path -get_module_name(${CMAKE_CURRENT_LIST_DIR}) - -# Exclude test module from all build -set_target_properties( - ${MODULE_NAME} - PROPERTIES - EXCLUDE_FROM_ALL TRUE -) diff --git a/Os/Stubs/FileStubs.hpp b/Os/Stubs/FileStubs.hpp deleted file mode 100644 index 32c7701f4c..0000000000 --- a/Os/Stubs/FileStubs.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * FileStubs.hpp - * - * Created on: Sep 28, 2015 - * Author: tcanham - */ - -#ifndef STUBS_FILESTUBS_HPP_ -#define STUBS_FILESTUBS_HPP_ - -#include - -namespace Os { - - // Interceptors are used to intercept calls to the Os::File methods. - // During a test, a callback can be registered with a pointer value - // that is returned with the subsequent call. The pointer can be used - // to store an instance of a test class to get back to the instance - // that is doing the testing. If the interceptor returns true, - // the regular call will be continued. This allows a particular - // read in a sequence of reads to be modified while allowing previous - // ones to complete normally. - - typedef bool (*OpenInterceptor)(Os::File::Status &status, const char* fileName, Os::File::Mode mode, void* ptr); - typedef bool (*ReadInterceptor)(Os::File::Status &status, void * buffer, NATIVE_INT_TYPE &size, bool waitForFull, void* ptr); - typedef bool (*WriteInterceptor)(Os::File::Status &status, const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone, void* ptr); - typedef bool (*SeekInterceptor)(Os::File::Status &status, NATIVE_INT_TYPE offset, bool absolute, void* ptr); - - void registerReadInterceptor(ReadInterceptor funcPtr, void* ptr); - void clearReadInterceptor(); - - void registerWriteInterceptor(WriteInterceptor funcPtr, void* ptr); - void clearWriteInterceptor(); - - void registerOpenInterceptor(OpenInterceptor funcPtr, void* ptr); - void clearOpenInterceptor(); - - void registerSeekInterceptor(SeekInterceptor funcPtr, void* ptr); - void clearSeekInterceptor(); - - void setLastError(NATIVE_INT_TYPE error); - -} - - -#endif /* STUBS_FILESTUBS_HPP_ */ diff --git a/Os/Stubs/Linux/FileStub.cpp b/Os/Stubs/Linux/FileStub.cpp deleted file mode 100644 index 8b589cded7..0000000000 --- a/Os/Stubs/Linux/FileStub.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace Os { - - static ReadInterceptor readInterceptor = nullptr; - static void *readInterceptorPtr = nullptr; - - static WriteInterceptor writeInterceptor = nullptr; - static void *writeInterceptorPtr = nullptr; - - static OpenInterceptor openInterceptor = nullptr; - static void *openInterceptorPtr = nullptr; - - static SeekInterceptor seekInterceptor = nullptr; - static void *seekInterceptorPtr = nullptr; - - static NATIVE_INT_TYPE lastError = 0; - - void registerReadInterceptor(ReadInterceptor funcPtr, void *ptr) { - readInterceptor = funcPtr; - readInterceptorPtr = ptr; - } - - void clearReadInterceptor() { - readInterceptor = nullptr; - } - - void registerWriteInterceptor(WriteInterceptor funcPtr, void *ptr) { - writeInterceptor = funcPtr; - writeInterceptorPtr = ptr; - } - - void clearWriteInterceptor() { - writeInterceptor = nullptr; - } - - void registerOpenInterceptor(OpenInterceptor funcPtr, void *ptr) { - openInterceptor = funcPtr; - openInterceptorPtr = ptr; - } - - void clearOpenInterceptor() { - openInterceptor = nullptr; - } - - void registerSeekInterceptor(SeekInterceptor funcPtr, void *ptr) { - seekInterceptor = funcPtr; - seekInterceptorPtr = ptr; - } - - void clearSeekInterceptor() { - seekInterceptor = nullptr; - } - - void setLastError(NATIVE_INT_TYPE error) { - lastError = error; - } - - - File::File() :m_fd(0),m_mode(OPEN_NO_MODE),m_lastError(0) { - } - - File::~File() { - if (this->m_mode != OPEN_NO_MODE) { - this->close(); - } - } - - File::Status File::open(const char* fileName, File::Mode mode) { - return this->open(fileName, mode, true); - } - - File::Status File::open(const char* fileName, File::Mode mode, bool include_excl) { - - if (openInterceptor) { - File::Status stat; - if (not openInterceptor(stat,fileName,mode,openInterceptorPtr)) { - return stat; - } - } - - NATIVE_INT_TYPE flags = 0; - Status stat = OP_OK; - - switch (mode) { - case OPEN_READ: - flags = O_RDONLY; - break; - case OPEN_WRITE: - flags = O_WRONLY | O_CREAT | O_TRUNC; - break; - case OPEN_SYNC_WRITE: - flags = O_WRONLY | O_CREAT | O_SYNC; - break; - case OPEN_SYNC_DIRECT_WRITE: - flags = O_WRONLY | O_CREAT | O_DSYNC -#ifdef __linux__ - | O_DIRECT; -#else - ; -#endif - break; - case OPEN_CREATE: - flags = O_WRONLY | O_CREAT | O_TRUNC; - if(include_excl) { - flags |= O_EXCL; - } - break; - default: - FW_ASSERT(0,mode); - break; - } - - NATIVE_INT_TYPE userFlags = -#ifdef __VXWORKS__ - 0; -#else - S_IRUSR|S_IWRITE; -#endif - NATIVE_INT_TYPE fd = ::open(fileName,flags,userFlags); - - if (-1 == fd) { - this->m_lastError = errno; - switch (errno) { - case ENOSPC: - stat = NO_SPACE; - break; - case ENOENT: - stat = DOESNT_EXIST; - break; - case EACCES: - stat = NO_PERMISSION; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - this->m_mode = mode; - this->m_fd = fd; - return stat; - } - - File::Status File::prealloc(NATIVE_INT_TYPE offset, NATIVE_INT_TYPE len) { - // make sure it has been opened - if (OPEN_NO_MODE == this->m_mode) { - return NOT_OPENED; - } - - File::Status fileStatus = OP_OK; - -#ifdef __linux__ - NATIVE_INT_TYPE stat = posix_fallocate(this->m_fd, offset, len); - - if (stat) { - switch (stat) { - case ENOSPC: - case EFBIG: - fileStatus = NO_SPACE; - break; - case EBADF: - fileStatus = DOESNT_EXIST; - break; - default: - fileStatus = OTHER_ERROR; - break; - } - } -#endif - - return fileStatus; - } - - File::Status File::seek(NATIVE_INT_TYPE offset, bool absolute) { - - if (seekInterceptor) { - File::Status stat; - if (not seekInterceptor(stat,offset,absolute,seekInterceptorPtr)) { - return stat; - } - } - - - // make sure it has been opened - if (OPEN_NO_MODE == this->m_mode) { - return NOT_OPENED; - } - - Status stat = OP_OK; - NATIVE_INT_TYPE whence = absolute?SEEK_SET:SEEK_CUR; - - off_t act_off = ::lseek(this->m_fd,offset,whence); - - // No error would be a normal one for this simple - // class, so return other error - if (act_off != offset) { - if (-1 == act_off) { - this->m_lastError = errno; - stat = OTHER_ERROR; - } else { - stat = BAD_SIZE; - } - } - - return stat; - } - - File::Status File::read(void * buffer, NATIVE_INT_TYPE &size, bool waitForFull) { - - FW_ASSERT(buffer); - - if (readInterceptor) { - File::Status stat; - if (not readInterceptor(stat,buffer,size,waitForFull,readInterceptorPtr)) { - return stat; - } - } - - // make sure it has been opened - if (OPEN_NO_MODE == this->m_mode) { - return NOT_OPENED; - } - - NATIVE_INT_TYPE accSize = 0; // accumulated size - - Status stat = OP_OK; - - NATIVE_INT_TYPE maxIters = size*2; // loop limit; couldn't block more times than number of bytes - - while (maxIters > 0) { - - ssize_t readSize = ::read(this->m_fd, -#ifdef __VXWORKS__ - static_cast(buffer) -#else - buffer -#endif - ,size-accSize); - - if (readSize != size-accSize) { - // could be an error - if (-1 == readSize) { - switch (errno) { - case EINTR: // read was interrupted - maxIters--; // decrement loop count - continue; - default: - stat = OTHER_ERROR; - break; - } - this->m_lastError = errno; - accSize = 0; - break; // break out of while loop - } else if (0 == readSize) { // end of file - accSize = 0; - break; - } else { // partial read so adjust read point and size - accSize += readSize; - if (not waitForFull) { - break; // break out of while loop - } else { - // in order to move the pointer ahead, we need to cast it - U8* charPtr = static_cast(buffer); - charPtr = &charPtr[readSize]; - buffer = static_cast(charPtr); - } - maxIters--; // decrement loop count - } - - } else { // got number we wanted - accSize += readSize; - break; // break out of while loop - } - - maxIters--; // decrement loop count - - } // end read while loop - - // make sure we didn't exceed loop count - FW_ASSERT(maxIters > 0); - - size = accSize; - - return stat; - } - - File::Status File::write(const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone) { - - if (writeInterceptor) { - File::Status stat; - if (not writeInterceptor(stat,buffer,size,waitForDone,writeInterceptorPtr)) { - return stat; - } - } - - // make sure it has been opened - if (OPEN_NO_MODE == this->m_mode) { - return NOT_OPENED; - } - - Status stat = OP_OK; - // just check for EINTR - NATIVE_INT_TYPE maxIters = size*2; // loop limit; couldn't block more times than number of bytes - while (maxIters > 0) { - NATIVE_INT_TYPE writeSize = ::write(this->m_fd, -#ifdef __VXWORKS__ - static_cast(const_cast(buffer)) // Ugly, but that's how VxWorks likes to roll... -#else - buffer -#endif - ,size); - if (-1 == writeSize) { - switch (errno) { - case EINTR: // write was interrupted - maxIters--; // decrement loop count - continue; - case ENOSPC: - stat = NO_SPACE; - break; - default: - stat = OTHER_ERROR; - break; - } - this->m_lastError = errno; - break; // break out of while loop - } else { - size = writeSize; - break; // break out of while loop - } - } - - return stat; - } - - // NOTE(mereweth) - see http://lkml.iu.edu/hypermail/linux/kernel/1005.2/01845.html - // recommendation from Linus Torvalds, but doesn't seem to be that fast - File::Status File::bulkWrite(const void * buffer, NATIVE_UINT_TYPE &totalSize, - NATIVE_INT_TYPE chunkSize) { -#ifdef __linux__ - const NATIVE_UINT_TYPE startPosition = lseek(this->m_fd, 0, SEEK_CUR); -#endif - NATIVE_UINT_TYPE newBytesWritten = 0; - - for (NATIVE_UINT_TYPE idx = 0; idx < totalSize; idx += chunkSize) { - NATIVE_INT_TYPE size = chunkSize; - // if we're on the last iteration and length isn't a multiple of chunkSize - if (idx + chunkSize > totalSize) { - size = totalSize - idx; - } - const NATIVE_INT_TYPE toWrite = size; - const Os::File::Status fileStatus = this->write(buffer, size, false); - if (!(fileStatus == Os::File::OP_OK - && size == static_cast(toWrite))) { - totalSize = newBytesWritten; - return fileStatus; - } - -#ifdef __linux__ - sync_file_range(this->m_fd, - startPosition + newBytesWritten, - chunkSize, - SYNC_FILE_RANGE_WRITE); - - if (newBytesWritten) { - sync_file_range(this->m_fd, - startPosition + newBytesWritten - chunkSize, - chunkSize, - SYNC_FILE_RANGE_WAIT_BEFORE - | SYNC_FILE_RANGE_WRITE - | SYNC_FILE_RANGE_WAIT_AFTER); - } -#endif - - newBytesWritten += toWrite; - } - - totalSize = newBytesWritten; - return OP_OK; - } - - File::Status File::flush() { - // make sure it has been opened - if (OPEN_NO_MODE == this->m_mode) { - return NOT_OPENED; - } - - File::Status stat = OP_OK; - - if (-1 == fsync(this->m_fd)) { - switch (errno) { - case ENOSPC: - stat = NO_SPACE; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - return stat; - } - - void File::close() { - (void)::close(this->m_fd); - this->m_mode = OPEN_NO_MODE; - } - - NATIVE_INT_TYPE File::getLastError() { - return lastError; - } - - const char* File::getLastErrorString() { - return strerror(this->m_lastError); - } - - - -} diff --git a/Os/ValidateFileCommon.cpp b/Os/ValidateFileCommon.cpp index 39b69b5325..9006e39076 100644 --- a/Os/ValidateFileCommon.cpp +++ b/Os/ValidateFileCommon.cpp @@ -18,10 +18,10 @@ namespace Os { // Get the file size: FileSystem::Status fs_status; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; fs_status = FileSystem::getFileSize(fileName, fileSize); //!< gets the size of the file (in bytes) at location path // fileSize will be used as a NATIVE_INT_TYPE below and thus must cast cleanly to that type - if( FileSystem::OP_OK != fs_status || static_cast(static_cast(fileSize)) != fileSize) { + if( FileSystem::OP_OK != fs_status) { return File::BAD_SIZE; } const NATIVE_INT_TYPE max_itr = static_cast(fileSize/VFILE_HASH_CHUNK_SIZE + 1); @@ -30,12 +30,12 @@ namespace Os { Utils::Hash hash; hash.init(); U8 buffer[VFILE_HASH_CHUNK_SIZE]; - NATIVE_INT_TYPE size = 0; - NATIVE_INT_TYPE cnt = 0; + FwSignedSizeType size = 0; + FwSignedSizeType cnt = 0; while( cnt <= max_itr ) { // Read out chunk from file: size = sizeof(buffer); - status = file.read(&buffer, size, false); + status = file.read(buffer, size, Os::File::WaitType::NO_WAIT); if( File::OP_OK != status ) { return status; } @@ -74,8 +74,8 @@ namespace Os { // Read hash from checksum file: unsigned char savedHash[HASH_DIGEST_LENGTH]; - NATIVE_INT_TYPE size = hashBuffer.getBuffCapacity(); - status = hashFile.read(&savedHash[0], size); + FwSignedSizeType size = hashBuffer.getBuffCapacity(); + status = hashFile.read(savedHash, size); if( File::OP_OK != status ) { return status; } @@ -101,8 +101,8 @@ namespace Os { } // Write out the hash - NATIVE_INT_TYPE size = hashBuffer.getBuffLength(); - status = hashFile.write(hashBuffer.getBuffAddr(), size, false); + FwSignedSizeType size = hashBuffer.getBuffLength(); + status = hashFile.write(hashBuffer.getBuffAddr(), size, Os::File::WaitType::NO_WAIT); if( File::OP_OK != status ) { return status; } diff --git a/Os/test/ut/OsFileSystemTest.cpp b/Os/test/ut/OsFileSystemTest.cpp index 54608a4b69..4ea9aedf74 100644 --- a/Os/test/ut/OsFileSystemTest.cpp +++ b/Os/test/ut/OsFileSystemTest.cpp @@ -12,197 +12,197 @@ void testTestFileSystem() { - Os::FileSystem::Status file_sys_status; - Os::File::Status file_status; - Os::File test_file = Os::File(); - const char test_file_name1[] = "test_file1"; - const char test_file_name2[] = "test_file2"; - const char test_dir1[] = "./test_dir1"; - const char test_dir2[] = "./test_dir2"; - const char up_dir[] = "../"; - const char cur_dir[] = "."; - struct stat info; - FwSizeType file_size = 42; - U32 file_count = 0; - const char test_string[] = "This is a test file."; - NATIVE_INT_TYPE test_string_len = 0; - - - printf("Creating %s directory\n", test_dir1); - if ((file_sys_status = Os::FileSystem::createDirectory(test_dir1)) != Os::FileSystem::OP_OK) { - printf("\tFailed to create directory: %s\n", test_dir1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_dir1, &info),0); - - // Check directory file count - printf("Checking directory file count of %s is 0.\n", test_dir1); - if ((file_sys_status = Os::FileSystem::getFileCount(test_dir1, file_count)) != Os::FileSystem::OP_OK) { - printf("\tFailed to get file count of %s\n", test_dir1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(file_count,0); - - printf("Moving directory (%s) to (%s).\n", test_dir1, test_dir2); - if ((file_sys_status = Os::FileSystem::moveFile(test_dir1, test_dir2)) != Os::FileSystem::OP_OK) { - printf("\tFailed to move directory (%s) to (%s).\n", test_dir1, test_dir2); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_dir1, &info),-1); - ASSERT_EQ(stat(test_dir2, &info),0); - - printf("Trying to copy directory (%s) to (%s).\n", test_dir2, test_dir1); - if ((file_sys_status = Os::FileSystem::copyFile(test_dir2, test_dir1)) == Os::FileSystem::OP_OK) { - printf("\tShould not have been able to copy a directory %s to %s.\n", test_dir2, test_dir1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_dir1, &info),-1); - ASSERT_EQ(stat(test_dir2, &info),0); - - printf("Removing directory %s\n", test_dir2); - if ((file_sys_status = Os::FileSystem::removeDirectory(test_dir2)) != Os::FileSystem::OP_OK) { - printf("\tFailed to remove directory: %s\n", test_dir2); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_dir2, &info),-1); - - //Create a file - printf("Creating test file (%s)\n", test_file_name1); - if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_WRITE)) != Os::File::OP_OK) { - printf("\tFailed to create file: %s\n", test_file_name1); - printf("\tReturn status: %d\n", file_status); - ASSERT_TRUE(0); - } - - test_string_len = sizeof(test_string); - - printf("Writing to test file (%s) for testing.\n", test_file_name1); - if((file_status = test_file.write(test_string, test_string_len, true)) != Os::File::OP_OK) { - printf("\tFailed to write to file: %s\n.", test_file_name1); - printf("\tReturn status: %d\n", file_status); - ASSERT_TRUE(0); - } - - //Close test file - test_file.close(); - - file_size = 42; - //Get file size - printf("Checking file size of %s.\n", test_file_name1); - if ((file_sys_status = Os::FileSystem::getFileSize(test_file_name1, file_size)) != Os::FileSystem::OP_OK) { - printf("\tFailed to get file size of %s\n", test_file_name1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(file_size,sizeof(test_string)); - - printf("Copying file (%s) to (%s).\n", test_file_name1, test_file_name2); - if ((file_sys_status = Os::FileSystem::copyFile(test_file_name1, test_file_name2)) != Os::FileSystem::OP_OK) { - printf("\tFailed to copy file (%s) to (%s)\n", test_file_name1, test_file_name2); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_file_name1, &info),0); - ASSERT_EQ(stat(test_file_name2, &info),0); - - char file_buf1[64]; - char file_buf2[64]; - // Read the two files and make sure they are the same - test_string_len = sizeof(test_string); - test_file.open(test_file_name1, Os::File::OPEN_READ); - test_file.read(&file_buf1, test_string_len, false); - test_file.close(); - - test_string_len = sizeof(test_string); - test_file.open(test_file_name2, Os::File::OPEN_READ); - test_file.read(&file_buf2, test_string_len, false); - test_file.close(); - - ASSERT_EQ(strcmp(file_buf1, file_buf2),0); - - printf("Removing test file 1 (%s)\n", test_file_name1); - if ((file_sys_status = Os::FileSystem::removeFile(test_file_name1)) != Os::FileSystem::OP_OK) { - printf("\tFailed to remove file (%s)\n", test_file_name1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_file_name1, &info),-1); - - printf("Removing test file 2 (%s)\n", test_file_name2); - if ((file_sys_status = Os::FileSystem::removeFile(test_file_name2)) != Os::FileSystem::OP_OK) { - printf("\tFailed to remove file (%s)\n", test_file_name2); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_file_name2, &info),-1); - - printf("Getting the number of files in (%s)\n", cur_dir); - if ((file_sys_status = Os::FileSystem::getFileCount(cur_dir, file_count)) != Os::FileSystem::OP_OK) { - printf("\tFailed to get number of files in (%s)\n", cur_dir); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_TRUE(file_count > 0); - - printf("Reading the files in (%s)\n", cur_dir); - const int num_str = 5; + Os::FileSystem::Status file_sys_status; + Os::File::Status file_status; + Os::File test_file = Os::File(); + const char test_file_name1[] = "test_file1"; + const char test_file_name2[] = "test_file2"; + const char test_dir1[] = "./test_dir1"; + const char test_dir2[] = "./test_dir2"; + const char up_dir[] = "../"; + const char cur_dir[] = "."; + struct stat info; + FwSignedSizeType file_size = 42; + U32 file_count = 0; + const U8 test_string[] = "This is a test file."; + FwSignedSizeType test_string_len = 0; + + + printf("Creating %s directory\n", test_dir1); + if ((file_sys_status = Os::FileSystem::createDirectory(test_dir1)) != Os::FileSystem::OP_OK) { + printf("\tFailed to create directory: %s\n", test_dir1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_dir1, &info),0); + + // Check directory file count + printf("Checking directory file count of %s is 0.\n", test_dir1); + if ((file_sys_status = Os::FileSystem::getFileCount(test_dir1, file_count)) != Os::FileSystem::OP_OK) { + printf("\tFailed to get file count of %s\n", test_dir1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(file_count,0); + + printf("Moving directory (%s) to (%s).\n", test_dir1, test_dir2); + if ((file_sys_status = Os::FileSystem::moveFile(test_dir1, test_dir2)) != Os::FileSystem::OP_OK) { + printf("\tFailed to move directory (%s) to (%s).\n", test_dir1, test_dir2); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_dir1, &info),-1); + ASSERT_EQ(stat(test_dir2, &info),0); + + printf("Trying to copy directory (%s) to (%s).\n", test_dir2, test_dir1); + if ((file_sys_status = Os::FileSystem::copyFile(test_dir2, test_dir1)) == Os::FileSystem::OP_OK) { + printf("\tShould not have been able to copy a directory %s to %s.\n", test_dir2, test_dir1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_dir1, &info),-1); + ASSERT_EQ(stat(test_dir2, &info),0); + + printf("Removing directory %s\n", test_dir2); + if ((file_sys_status = Os::FileSystem::removeDirectory(test_dir2)) != Os::FileSystem::OP_OK) { + printf("\tFailed to remove directory: %s\n", test_dir2); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_dir2, &info),-1); + + //Create a file + printf("Creating test file (%s)\n", test_file_name1); + if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_WRITE)) != Os::File::OP_OK) { + printf("\tFailed to create file: %s\n", test_file_name1); + printf("\tReturn status: %d\n", file_status); + ASSERT_TRUE(0); + } + + test_string_len = sizeof(test_string); + + printf("Writing to test file (%s) for testing.\n", test_file_name1); + if((file_status = test_file.write(test_string, test_string_len, Os::File::WaitType::WAIT)) != Os::File::OP_OK) { + printf("\tFailed to write to file: %s\n.", test_file_name1); + printf("\tReturn status: %d\n", file_status); + ASSERT_TRUE(0); + } + + //Close test file + test_file.close(); + + file_size = 42; + //Get file size + printf("Checking file size of %s.\n", test_file_name1); + if ((file_sys_status = Os::FileSystem::getFileSize(test_file_name1, file_size)) != Os::FileSystem::OP_OK) { + printf("\tFailed to get file size of %s\n", test_file_name1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(file_size,sizeof(test_string)); + + printf("Copying file (%s) to (%s).\n", test_file_name1, test_file_name2); + if ((file_sys_status = Os::FileSystem::copyFile(test_file_name1, test_file_name2)) != Os::FileSystem::OP_OK) { + printf("\tFailed to copy file (%s) to (%s)\n", test_file_name1, test_file_name2); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_file_name1, &info),0); + ASSERT_EQ(stat(test_file_name2, &info),0); + + unsigned char file_buf1[64]; + unsigned char file_buf2[64]; + // Read the two files and make sure they are the same + test_string_len = sizeof(test_string); + test_file.open(test_file_name1, Os::File::OPEN_READ); + test_file.read(file_buf1, test_string_len, Os::File::WaitType::NO_WAIT); + test_file.close(); + + test_string_len = sizeof(test_string); + test_file.open(test_file_name2, Os::File::OPEN_READ); + test_file.read(file_buf2, test_string_len, Os::File::WaitType::NO_WAIT); + test_file.close(); + + ASSERT_EQ(::strcmp(reinterpret_cast(file_buf1), reinterpret_cast(file_buf2)),0); + + printf("Removing test file 1 (%s)\n", test_file_name1); + if ((file_sys_status = Os::FileSystem::removeFile(test_file_name1)) != Os::FileSystem::OP_OK) { + printf("\tFailed to remove file (%s)\n", test_file_name1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_file_name1, &info),-1); + + printf("Removing test file 2 (%s)\n", test_file_name2); + if ((file_sys_status = Os::FileSystem::removeFile(test_file_name2)) != Os::FileSystem::OP_OK) { + printf("\tFailed to remove file (%s)\n", test_file_name2); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_file_name2, &info),-1); + + printf("Getting the number of files in (%s)\n", cur_dir); + if ((file_sys_status = Os::FileSystem::getFileCount(cur_dir, file_count)) != Os::FileSystem::OP_OK) { + printf("\tFailed to get number of files in (%s)\n", cur_dir); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_TRUE(file_count > 0); + + printf("Reading the files in (%s)\n", cur_dir); + const int num_str = 5; U32 num_2 = num_str; - Fw::String str_array[num_str]; - if ((file_sys_status = Os::FileSystem::readDirectory(cur_dir, num_str, str_array, num_2)) != Os::FileSystem::OP_OK) { - printf("\tFailed to read files in (%s)\n", cur_dir); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - else { - for (int i = 0; i < num_str; ++i) { - printf("%s\n",str_array[i].toChar()); - } - } - - printf("Changing working directory to (%s)\n", up_dir); - if ((file_sys_status = Os::FileSystem::changeWorkingDirectory(up_dir)) != Os::FileSystem::OP_OK) { - printf("\tFailed to change working directory to: %s\n", up_dir); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - - printf("Checking current working directory is (%s)\n", up_dir); + Fw::String str_array[num_str]; + if ((file_sys_status = Os::FileSystem::readDirectory(cur_dir, num_str, str_array, num_2)) != Os::FileSystem::OP_OK) { + printf("\tFailed to read files in (%s)\n", cur_dir); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + else { + for (int i = 0; i < num_str; ++i) { + printf("%s\n",str_array[i].toChar()); + } + } + + printf("Changing working directory to (%s)\n", up_dir); + if ((file_sys_status = Os::FileSystem::changeWorkingDirectory(up_dir)) != Os::FileSystem::OP_OK) { + printf("\tFailed to change working directory to: %s\n", up_dir); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + + printf("Checking current working directory is (%s)\n", up_dir); char dir_buff[256]; getcwd(dir_buff, 256); - printf("Current dir: %s\n", dir_buff); - - //Create a file in OPEN_CREATE mode - printf("Creating test file (%s)\n", test_file_name1); - if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_CREATE)) != Os::File::OP_OK) { - printf("\tFailed to OPEN_CREATE file: %s\n", test_file_name1); - printf("\tReturn status: %d\n", file_status); - ASSERT_TRUE(0); - } - - //Close test file - test_file.close(); - - //Should not be able to OPEN_CREATE file since it already exist and we have - //include_excl enabled - printf("Creating test file (%s)\n", test_file_name1); - if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_CREATE)) != Os::File::FILE_EXISTS) { - printf("\tFailed to not to overwrite existing file: %s\n", test_file_name1); - printf("\tReturn status: %d\n", file_status); - ASSERT_TRUE(0); - } - - printf("Removing test file 1 (%s)\n", test_file_name1); - if ((file_sys_status = Os::FileSystem::removeFile(test_file_name1)) != Os::FileSystem::OP_OK) { - printf("\tFailed to remove file (%s)\n", test_file_name1); - printf("\tReturn status: %d\n", file_sys_status); - ASSERT_TRUE(0); - } - ASSERT_EQ(stat(test_file_name1, &info),-1); + printf("Current dir: %s\n", dir_buff); + + //Create a file in OPEN_CREATE mode + printf("Creating test file (%s)\n", test_file_name1); + if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_CREATE)) != Os::File::OP_OK) { + printf("\tFailed to OPEN_CREATE file: %s\n", test_file_name1); + printf("\tReturn status: %d\n", file_status); + ASSERT_TRUE(0); + } + + //Close test file + test_file.close(); + + //Should not be able to OPEN_CREATE file since it already exist and we have + //include_excl enabled + printf("Creating test file (%s)\n", test_file_name1); + if ((file_status = test_file.open(test_file_name1, Os::File::OPEN_CREATE)) != Os::File::FILE_EXISTS) { + printf("\tFailed to not to overwrite existing file: %s\n", test_file_name1); + printf("\tReturn status: %d\n", file_status); + ASSERT_TRUE(0); + } + + printf("Removing test file 1 (%s)\n", test_file_name1); + if ((file_sys_status = Os::FileSystem::removeFile(test_file_name1)) != Os::FileSystem::OP_OK) { + printf("\tFailed to remove file (%s)\n", test_file_name1); + printf("\tReturn status: %d\n", file_sys_status); + ASSERT_TRUE(0); + } + ASSERT_EQ(stat(test_file_name1, &info),-1); } diff --git a/Os/test/ut/OsValidateFileTest.cpp b/Os/test/ut/OsValidateFileTest.cpp index db749be20f..adf41d502e 100644 --- a/Os/test/ut/OsValidateFileTest.cpp +++ b/Os/test/ut/OsValidateFileTest.cpp @@ -48,7 +48,7 @@ void testValidateFile(const char* fileName) { // Get file size: printf("Checking file size of %s.\n", hashFileName); - FwSizeType fileSize=0; + FwSignedSizeType fileSize=0; fsStatus = Os::FileSystem::getFileSize(hashFileName, fileSize); if( Os::FileSystem::OP_OK != fsStatus ) { printf("\tFailed to get file size of %s\n", hashFileName); diff --git a/Os/test/ut/file/CommonTests.cpp b/Os/test/ut/file/CommonTests.cpp new file mode 100644 index 0000000000..845fdbeb26 --- /dev/null +++ b/Os/test/ut/file/CommonTests.cpp @@ -0,0 +1,484 @@ +// ====================================================================== +// \title Os/test/ut/file/CommonFileTests.cpp +// \brief common test implementations +// ====================================================================== +#include "Os/test/ut/file/CommonTests.hpp" +#include +#include "Os/File.hpp" + +static const U32 RANDOM_BOUND = 1000; + +Os::Test::File::Tester::Tester() { + // Wipe out the file system with a fresh copy + SyntheticFile::setFileSystem(std::unique_ptr(new SyntheticFileSystem())); +} + +Functionality::Functionality() : tester(Os::Test::File::get_tester_implementation()) {} + +void Functionality::SetUp() { + Os::Test::File::setUp(false); +} + +void Functionality::TearDown() { + Os::Test::File::tearDown(); +} + +void FunctionalIO::SetUp() { + // Check that the tester supports functional tests + if (this->tester->functional()) { + this->Functionality::SetUp(); + } else { + GTEST_SKIP() << "Tester does not support functional i/o testing"; + } +} + +// Ensure that open mode changes work reliably +TEST_F(Functionality, OpenWithCreation) { + Os::Test::File::Tester::OpenFileCreate rule(false); + rule.apply(*tester); +} + +// Ensure that close mode changes work reliably +TEST_F(Functionality, Close) { + Os::Test::File::Tester::OpenFileCreate create_rule(false); + Os::Test::File::Tester::CloseFile close_rule; + create_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Ensure that the assignment operator works correctly +TEST_F(Functionality, AssignmentOperator) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CopyAssignment copy_rule; + Os::Test::File::Tester::CloseFile close_rule; + open_rule.apply(*tester); + copy_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Ensure the copy constructor works correctly +TEST_F(Functionality, CopyConstructor) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CopyConstruction copy_rule; + Os::Test::File::Tester::CloseFile close_rule; + open_rule.apply(*tester); + copy_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Ensure that open on existence works +TEST_F(FunctionalIO, OpenWithCreationExists) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CloseFile close_rule; + open_rule.apply(*tester); + close_rule.apply(*tester); + open_rule.apply(*tester); +} + +// Ensure that open on existence with overwrite works +TEST_F(Functionality, OpenWithCreationOverwrite) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::OpenFileCreateOverwrite open_overwrite(false); + Os::Test::File::Tester::CloseFile close_rule; + open_rule.apply(*tester); + close_rule.apply(*tester); + open_overwrite.apply(*tester); +} + +// Ensure that open mode changes work reliably +TEST_F(Functionality, OpenInvalidModes) { + Os::Test::File::Tester::OpenFileCreate original_open(false); + Os::Test::File::Tester::OpenInvalidModes invalid_open; + original_open.apply(*tester); + invalid_open.apply(*tester); +} + +// Ensure that Os::File properly refuses preallocate calls when not open +TEST_F(Functionality, PreallocateWithoutOpen) { + Os::Test::File::Tester::PreallocateWithoutOpen rule; + rule.apply(*tester); +} + +// Ensure that Os::File properly refuses seek calls when not open +TEST_F(Functionality, SeekWithoutOpen) { + Os::Test::File::Tester::SeekWithoutOpen rule; + rule.apply(*tester); +} + +// Ensure that Os::File properly refuses seek calls when not open +TEST_F(FunctionalIO, SeekInvalidSize) { + Os::Test::File::Tester::OpenFileCreate original_open(false); + Os::Test::File::Tester::SeekInvalidSize rule; + original_open.apply(*tester); + rule.apply(*tester); +} + +// Ensure that Os::File properly refuses flush calls when not open and when reading +TEST_F(Functionality, FlushInvalidModes) { + Os::Test::File::Tester::FlushInvalidModes flush_rule; + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + + // Test flush in closed state + flush_rule.apply(*tester); + + // Used to create the test file an open in read-mode correctly + open_rule.apply(*tester); + close_rule.apply(*tester); + tester->assert_file_closed(); + open_read.apply(*tester); + tester->assert_file_opened(tester->m_current_path); + + // Check that a read-mode file cannot flush + flush_rule.apply(*tester); +} + +// Ensure that Os::File properly refuses read calls when not open and when reading +TEST_F(Functionality, ReadInvalidModes) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForWrite open_write; + Os::Test::File::Tester::ReadInvalidModes read_rule; + + // Test read in closed state + read_rule.apply(*tester); + tester->assert_file_closed(); + + // Used to create the test file and ensure reads cannot happen in read-mode + open_rule.apply(*tester); + read_rule.apply(*tester); + close_rule.apply(*tester); + tester->assert_file_closed(); + + // Used to open (now existent) file in write-mode + open_write.apply(*tester); + tester->assert_file_opened(tester->m_current_path); + + // Check that a read won't work on write-mode data + read_rule.apply(*tester); +} + +// Ensure that Os::File properly refuses write calls when not open and when reading +TEST_F(Functionality, WriteInvalidModes) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + Os::Test::File::Tester::WriteInvalidModes write_rule; + + // Test write in closed state + write_rule.apply(*tester); + tester->assert_file_closed(); + + // Used to create the test file in read-mode correctly + open_rule.apply(*tester); + close_rule.apply(*tester); + tester->assert_file_closed(); + open_read.apply(*tester); + tester->assert_file_opened(tester->m_current_path); + + // Check that a write won't work on write-mode data + write_rule.apply(*tester); +} + +// Ensure a write followed by a read produces valid data +TEST_F(FunctionalIO, WriteReadBack) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + Os::Test::File::Tester::Read read_rule; + + open_rule.apply(*tester); + write_rule.apply(*tester); + close_rule.apply(*tester); + open_read.apply(*tester); + read_rule.apply(*tester); +} + +// Ensure a write followed by a read produces valid data +TEST_F(FunctionalIO, WriteReadSeek) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + Os::Test::File::Tester::Read read_rule; + Os::Test::File::Tester::Seek seek_rule; + + open_rule.apply(*tester); + write_rule.apply(*tester); + close_rule.apply(*tester); + open_read.apply(*tester); + read_rule.apply(*tester); + seek_rule.apply(*tester); +} + +// Ensure a write followed by a full crc produces valid results +TEST_F(FunctionalIO, WriteFullCrc) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + Os::Test::File::Tester::FullCrc crc_rule; + + open_rule.apply(*tester); + write_rule.apply(*tester); + close_rule.apply(*tester); + open_read.apply(*tester); + crc_rule.apply(*tester); +} + +// Ensure a write followed by a partial crc produces valid results +TEST_F(FunctionalIO, WritePartialCrc) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::CloseFile close_rule; + Os::Test::File::Tester::OpenForRead open_read; + Os::Test::File::Tester::IncrementalCrc crc_rule; + Os::Test::File::Tester::FinalizeCrc finalize_rule; + + open_rule.apply(*tester); + write_rule.apply(*tester); + close_rule.apply(*tester); + open_read.apply(*tester); + crc_rule.apply(*tester); + finalize_rule.apply(*tester); +} + +// Ensure a preallocate produces valid sizes +TEST_F(FunctionalIO, Flush) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::Flush flush_rule; + + open_rule.apply(*tester); + write_rule.apply(*tester); + flush_rule.apply(*tester); +} + +// Ensure a preallocate produces valid sizes +TEST_F(FunctionalIO, Preallocate) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::Preallocate preallocate_rule; + + open_rule.apply(*tester); + preallocate_rule.apply(*tester); +} + +// Randomized testing on the interfaces +TEST_F(Functionality, RandomizedInterfaceTesting) { + // Enumerate all rules and construct an instance of each + Os::Test::File::Tester::OpenFileCreate open_file_create_rule(true); + Os::Test::File::Tester::OpenFileCreateOverwrite open_file_create_overwrite_rule(true); + Os::Test::File::Tester::OpenForWrite open_for_write_rule(true); + Os::Test::File::Tester::CloseFile close_file_rule; + Os::Test::File::Tester::CopyConstruction copy_construction; + Os::Test::File::Tester::CopyAssignment copy_assignment; + Os::Test::File::Tester::OpenInvalidModes open_invalid_modes_rule; + Os::Test::File::Tester::PreallocateWithoutOpen preallocate_without_open_rule; + Os::Test::File::Tester::SeekWithoutOpen seek_without_open_rule; + Os::Test::File::Tester::FlushInvalidModes flush_invalid_modes_rule; + Os::Test::File::Tester::ReadInvalidModes read_invalid_modes_rule; + Os::Test::File::Tester::WriteInvalidModes write_invalid_modes_rule; + Os::Test::File::Tester::OpenIllegalPath open_illegal_path; + Os::Test::File::Tester::OpenIllegalMode open_illegal_mode; + Os::Test::File::Tester::PreallocateIllegalOffset preallocate_illegal_offset; + Os::Test::File::Tester::PreallocateIllegalLength preallocate_illegal_length; + Os::Test::File::Tester::SeekIllegal seek_illegal; + Os::Test::File::Tester::ReadIllegalBuffer read_illegal_buffer; + Os::Test::File::Tester::ReadIllegalSize read_illegal_size; + Os::Test::File::Tester::WriteIllegalBuffer write_illegal_buffer; + Os::Test::File::Tester::WriteIllegalSize write_illegal_size; + Os::Test::File::Tester::IncrementalCrcInvalidModes incremental_invalid_mode_rule; + Os::Test::File::Tester::FullCrcInvalidModes full_invalid_mode_rule; + + // Place these rules into a list of rules + STest::Rule* rules[] = { + &open_file_create_rule, + &open_file_create_overwrite_rule, + &open_for_write_rule, + &close_file_rule, + ©_assignment, + ©_construction, + &open_invalid_modes_rule, + &preallocate_without_open_rule, + &seek_without_open_rule, + &flush_invalid_modes_rule, + &read_invalid_modes_rule, + &write_invalid_modes_rule, + &open_illegal_path, + &open_illegal_mode, + &preallocate_illegal_offset, + &preallocate_illegal_length, + &seek_illegal, + &read_illegal_buffer, + &read_illegal_size, + &write_illegal_buffer, + &write_illegal_size, + &incremental_invalid_mode_rule, + &full_invalid_mode_rule + }; + + // Take the rules and place them into a random scenario + STest::RandomScenario random( + "Random Rules", + rules, + FW_NUM_ARRAY_ELEMENTS(rules) + ); + + // Create a bounded scenario wrapping the random scenario + STest::BoundedScenario bounded( + "Bounded Random Rules Scenario", + random, + RANDOM_BOUND/10 + ); + // Run! + const U32 numSteps = bounded.run(*tester); + printf("Ran %u steps.\n", numSteps); +} + +// Ensure a write followed by a read produces valid data +TEST_F(FunctionalIO, RandomizedTesting) { + // Enumerate all rules and construct an instance of each + Os::Test::File::Tester::OpenFileCreate open_file_create_rule(true); + Os::Test::File::Tester::OpenFileCreateOverwrite open_file_create_overwrite_rule(true); + Os::Test::File::Tester::OpenForWrite open_for_write_rule(true); + Os::Test::File::Tester::OpenForRead open_for_read_rule(true); + Os::Test::File::Tester::CloseFile close_file_rule; + Os::Test::File::Tester::Read read_rule; + Os::Test::File::Tester::Write write_rule; + Os::Test::File::Tester::Seek seek_rule; + Os::Test::File::Tester::Preallocate preallocate_rule; + Os::Test::File::Tester::Flush flush_rule; + Os::Test::File::Tester::CopyConstruction copy_construction; + Os::Test::File::Tester::CopyAssignment copy_assignment; + Os::Test::File::Tester::IncrementalCrc incremental_crc_rule; + Os::Test::File::Tester::FinalizeCrc finalize_crc_rule; + Os::Test::File::Tester::FullCrc full_crc_rule; + Os::Test::File::Tester::OpenInvalidModes open_invalid_modes_rule; + Os::Test::File::Tester::PreallocateWithoutOpen preallocate_without_open_rule; + Os::Test::File::Tester::SeekWithoutOpen seek_without_open_rule; + Os::Test::File::Tester::SeekInvalidSize seek_invalid_size; + Os::Test::File::Tester::FlushInvalidModes flush_invalid_modes_rule; + Os::Test::File::Tester::ReadInvalidModes read_invalid_modes_rule; + Os::Test::File::Tester::WriteInvalidModes write_invalid_modes_rule; + Os::Test::File::Tester::IncrementalCrcInvalidModes incremental_invalid_mode_rule; + Os::Test::File::Tester::FullCrcInvalidModes full_invalid_mode_rule; + + + // Place these rules into a list of rules + STest::Rule* rules[] = { + &open_file_create_rule, + &open_file_create_overwrite_rule, + &open_for_write_rule, + &open_for_read_rule, + &close_file_rule, + ©_assignment, + ©_construction, + &read_rule, + &write_rule, + &seek_rule, + &preallocate_rule, + &flush_rule, + &incremental_crc_rule, + &finalize_crc_rule, + &full_crc_rule, + &open_invalid_modes_rule, + &preallocate_without_open_rule, + &seek_without_open_rule, + &seek_invalid_size, + &flush_invalid_modes_rule, + &read_invalid_modes_rule, + &write_invalid_modes_rule, + &incremental_invalid_mode_rule, + &full_invalid_mode_rule + }; + + // Take the rules and place them into a random scenario + STest::RandomScenario random( + "Random Rules", + rules, + FW_NUM_ARRAY_ELEMENTS(rules) + ); + + // Create a bounded scenario wrapping the random scenario + STest::BoundedScenario bounded( + "Bounded Random Rules Scenario", + random, + RANDOM_BOUND + ); + // Run! + const U32 numSteps = bounded.run(*tester); + printf("Ran %u steps.\n", numSteps); +} + +// Ensure that Os::File properly refuses fullCrc when not in write mode +TEST_F(Functionality, FullCrcInvalidMode) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::FullCrcInvalidModes rule; + open_rule.apply(*tester); + rule.apply(*tester); +} + +// Ensure that Os::File properly refuses incrementalCrc when not in write mode +TEST_F(Functionality, IncrementalCrcInvalidMode) { + Os::Test::File::Tester::OpenFileCreate open_rule(false); + Os::Test::File::Tester::IncrementalCrcInvalidModes rule; + open_rule.apply(*tester); + rule.apply(*tester); +} + + +// Ensure open prevents nullptr as path +TEST_F(InvalidArguments, OpenBadPath) { + Os::Test::File::Tester::OpenIllegalPath rule; + rule.apply(*tester); +} + +// Ensure open prevents bad modes +TEST_F(InvalidArguments, OpenBadMode) { + Os::Test::File::Tester::OpenIllegalMode rule; + rule.apply(*tester); +} + +// Ensure preallocate prevents bad offset +TEST_F(InvalidArguments, PreallocateBadOffset) { + Os::Test::File::Tester::PreallocateIllegalOffset rule; + rule.apply(*tester); +} + +// Ensure preallocate prevents bad length +TEST_F(InvalidArguments, PreallocateBadLength) { + Os::Test::File::Tester::PreallocateIllegalLength rule; + rule.apply(*tester); +} + +// Ensure preallocate prevents bad length +TEST_F(InvalidArguments, SeekAbsoluteWithNegativeLength) { + Os::Test::File::Tester::SeekIllegal rule; + rule.apply(*tester); +} + +// Ensure read prevents bad buffer pointers +TEST_F(InvalidArguments, ReadInvalidBuffer) { + Os::Test::File::Tester::ReadIllegalBuffer rule; + rule.apply(*tester); +} + +// Ensure read prevents bad sizes +TEST_F(InvalidArguments, ReadInvalidSize) { + Os::Test::File::Tester::ReadIllegalSize rule; + rule.apply(*tester); +} + +// Ensure write prevents bad buffer pointers +TEST_F(InvalidArguments, WriteInvalidBuffer) { + Os::Test::File::Tester::WriteIllegalBuffer rule; + rule.apply(*tester); +} + +// Ensure write prevents bad sizes +TEST_F(InvalidArguments, WriteInvalidSize) { + Os::Test::File::Tester::WriteIllegalSize rule; + rule.apply(*tester); +} diff --git a/Os/test/ut/file/CommonTests.hpp b/Os/test/ut/file/CommonTests.hpp new file mode 100644 index 0000000000..ca61efc7be --- /dev/null +++ b/Os/test/ut/file/CommonTests.hpp @@ -0,0 +1,54 @@ +// ====================================================================== +// \title Os/test/ut/file/CommonFileTests.hpp +// \brief definitions used in common file testing +// ====================================================================== +#include +#include + +#ifndef OS_TEST_UT_COMMON_FILE_TESTS_HPP +#define OS_TEST_UT_COMMON_FILE_TESTS_HPP +namespace Os { +namespace Test { +namespace File { + +//! Set up function as defined by the unit test implementor +void setUp(bool requires_io //!< Does this test require functional io devices +); + +//! Tear down function as defined by the unit test implementor +void tearDown(); + +} // namespace File +} // namespace Test +} // namespace Os + +// Basic file tests +class Functionality : public ::testing::Test { + public: + //! Constructor + Functionality(); + + //! Setup function delegating to UT setUp function + void SetUp() override; + + //! Setup function delegating to UT tearDown function + void TearDown() override; + + //! Tester/state implementation + std::unique_ptr tester; +}; + +//! Interface testing +class Interface : public Functionality {}; + +//! Category of tests to check for invalid argument assertions +class InvalidArguments : public Functionality {}; + +//! Category of tests dependent on functional io +class FunctionalIO : public Functionality { + + //! Specialized setup method used to pass requirement for functional i/o + void SetUp() override; +}; + +#endif // OS_TEST_UT_COMMON_FILE_TESTS_HPP diff --git a/Os/test/ut/file/FileRules.cpp b/Os/test/ut/file/FileRules.cpp new file mode 100644 index 0000000000..c07260e35e --- /dev/null +++ b/Os/test/ut/file/FileRules.cpp @@ -0,0 +1,1116 @@ +// ====================================================================== +// \title Os/test/ut/file/MyRules.cpp +// \brief rule implementations for common testing +// ====================================================================== + +#include "RulesHeaders.hpp" +#include "STest/Pick/Pick.hpp" +extern "C" { +#include // borrow CRC +} + +// For testing, limit files to 32K +const FwSignedSizeType FILE_DATA_MAXIMUM = 32 * 1024; + +Os::File::Status Os::Test::File::Tester::shadow_open(const std::string &path, Os::File::Mode open_mode, bool overwrite) { + Os::File::Status status = this->m_shadow.open(path.c_str(), open_mode, overwrite ? Os::File::OverwriteType::OVERWRITE : Os::File::OverwriteType::NO_OVERWRITE); + if (Os::File::Status::OP_OK == status) { + this->m_current_path = path; + this->m_mode = open_mode; + this->m_independent_crc = Os::File::INITIAL_CRC; + } else { + this->m_current_path.clear(); + this->m_mode = Os::File::Mode::OPEN_NO_MODE; + } + return status; +} + +void Os::Test::File::Tester::shadow_close() { + this->m_shadow.close(); + this->m_current_path.clear(); + this->m_mode = Os::File::Mode::OPEN_NO_MODE; + // Checks on the shadow data to ensure consistency + ASSERT_TRUE(this->m_current_path.empty()); +} + +std::vector Os::Test::File::Tester::shadow_read(FwSignedSizeType size) { + std::vector output; + output.resize(size); + Os::File::Status status = m_shadow.read(output.data(), size, Os::File::WaitType::WAIT); + output.resize(size); + EXPECT_EQ(status, Os::File::Status::OP_OK); + return output; +} + +void Os::Test::File::Tester::shadow_write(const std::vector& write_data) { + FwSignedSizeType size = static_cast(write_data.size()); + FwSignedSizeType original_size = size; + Os::File::Status status = Os::File::OP_OK; + if (write_data.data() != nullptr) { + status = m_shadow.write(write_data.data(), size, Os::File::WaitType::WAIT); + } + ASSERT_EQ(status, Os::File::Status::OP_OK); + ASSERT_EQ(size, original_size); +} + +void Os::Test::File::Tester::shadow_seek(const FwSignedSizeType offset, const bool absolute) { + Os::File::Status status = m_shadow.seek(offset, absolute ?Os::File::SeekType::ABSOLUTE : Os::File::SeekType::RELATIVE); + ASSERT_EQ(status, Os::File::Status::OP_OK); +} + +void Os::Test::File::Tester::shadow_preallocate(const FwSignedSizeType offset, const FwSignedSizeType length) { + Os::File::Status status = m_shadow.preallocate(offset, length); + ASSERT_EQ(status, Os::File::Status::OP_OK); +} + +void Os::Test::File::Tester::shadow_flush() { + Os::File::Status status = m_shadow.flush(); + ASSERT_EQ(status, Os::File::Status::OP_OK); +} + +void Os::Test::File::Tester::shadow_crc(U32& crc) { + crc = this->m_independent_crc; + SyntheticFileData& data = *reinterpret_cast(this->m_shadow.getHandle()); + + // Calculate CRC on full file starting at m_pointer + for (FwSizeType i = data.m_pointer; i < data.m_data.size(); i++, this->m_shadow.seek(1, Os::File::SeekType::RELATIVE)) { + crc = update_crc_32(crc, static_cast(data.m_data.at(i))); + } + // Update tracking variables + this->m_independent_crc = Os::File::INITIAL_CRC; +} + +void Os::Test::File::Tester::shadow_partial_crc(FwSignedSizeType& size) { + SyntheticFileData data = *reinterpret_cast(this->m_shadow.getHandle()); + + // Calculate CRC on full file starting at m_pointer + const FwSizeType bound = FW_MIN(static_cast(data.m_pointer) + size, data.m_data.size()); + size = FW_MAX(0, static_cast(bound - data.m_pointer)); + for (FwSizeType i = data.m_pointer; i < bound; i++) { + this->m_independent_crc = update_crc_32(this->m_independent_crc, static_cast(data.m_data.at(i))); + this->m_shadow.seek(1, Os::File::SeekType::RELATIVE); + } +} + +void Os::Test::File::Tester::shadow_finalize(U32& crc) { + crc = this->m_independent_crc; + this->m_independent_crc = Os::File::INITIAL_CRC; +} + + +Os::Test::File::Tester::FileState Os::Test::File::Tester::current_file_state() { + Os::Test::File::Tester::FileState state; + // Invariant: mode must not be closed, or path must be nullptr + EXPECT_TRUE((Os::File::Mode::OPEN_NO_MODE != this->m_file.m_mode) || (nullptr == this->m_file.m_path)); + + // Read state when file is open + if (Os::File::Mode::OPEN_NO_MODE != this->m_file.m_mode) { + EXPECT_EQ(this->m_file.position(state.position), Os::File::Status::OP_OK); + EXPECT_EQ(this->m_file.size(state.size), Os::File::Status::OP_OK); + // Extra check to ensure size does not alter pointer + FwSignedSizeType new_position = -1; + EXPECT_EQ(this->m_file.position(new_position), Os::File::Status::OP_OK); + EXPECT_EQ(new_position, state.position); + } + return state; +} + +void Os::Test::File::Tester::assert_valid_mode_status(Os::File::Status &status) const { + if (Os::File::Mode::OPEN_NO_MODE == this->m_mode) { + ASSERT_EQ(status, Os::File::Status::NOT_OPENED); + } else { + ASSERT_EQ(status, Os::File::Status::INVALID_MODE); + } +} + + +void Os::Test::File::Tester::assert_file_consistent() { + // Ensure file mode + ASSERT_EQ(this->m_mode, this->m_file.m_mode); + if (this->m_file.m_path == nullptr) { + ASSERT_EQ(this->m_current_path, std::string("")); + } else { + // Ensure the state path matches the file path + std::string path = std::string(this->m_file.m_path); + ASSERT_EQ(path, this->m_current_path); + + // Check real file properties when able to do so + if (this->functional()) { + // File exists, check all properties + if (SyntheticFile::exists(this->m_current_path.c_str())) { + // Ensure the file pointer is consistent + FwSignedSizeType current_position = 0; + FwSignedSizeType shadow_position = 0; + ASSERT_EQ(this->m_file.position(current_position), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.position(shadow_position), Os::File::Status::OP_OK); + + ASSERT_EQ(current_position, shadow_position); + // Ensure the file size is consistent + FwSignedSizeType current_size = 0; + FwSignedSizeType shadow_size = 0; + ASSERT_EQ(this->m_file.size(current_size), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.size(shadow_size), Os::File::Status::OP_OK); + ASSERT_EQ(current_size, shadow_size); + } + // Does not exist + else { + ASSERT_FALSE(this->exists(this->m_current_path)); + } + } + } +} + +void Os::Test::File::Tester::assert_file_opened(const std::string &path, Os::File::Mode newly_opened_mode, bool overwrite) { + // Assert the that the file is opened in some mode + ASSERT_NE(this->m_file.m_mode, Os::File::Mode::OPEN_NO_MODE); + ASSERT_TRUE(this->m_file.isOpen()) << "`isOpen()` failed to indicate file is open"; + ASSERT_EQ(this->m_file.m_mode, this->m_mode); + + // When the open mode has been specified assert that is in an exact state + if (not path.empty() && Os::File::Mode::OPEN_NO_MODE != newly_opened_mode) { + // Assert file pointer always at beginning when functional + if (functional() ) { + FwSignedSizeType file_position = -1; + ASSERT_EQ(this->m_file.position(file_position), Os::File::Status::OP_OK); + ASSERT_EQ(file_position, 0); + } + ASSERT_EQ(std::string(this->m_file.m_path), path); + ASSERT_EQ(this->m_file.m_mode, newly_opened_mode) << "File is in unexpected mode"; + + // Check truncations + const bool truncate = (Os::File::Mode::OPEN_CREATE == newly_opened_mode) && overwrite; + if (truncate) { + if (this->functional()) { + FwSignedSizeType file_size = -1; + ASSERT_EQ(this->m_file.size(file_size), Os::File::Status::OP_OK); + ASSERT_EQ(file_size, 0); + } + } + } +} + +void Os::Test::File::Tester::assert_file_closed() { + ASSERT_EQ(this->m_file.m_mode, Os::File::Mode::OPEN_NO_MODE) << "File is in unexpected mode"; + ASSERT_FALSE(this->m_file.isOpen()) << "`isOpen()` failed to indicate file is open"; +} + +void Os::Test::File::Tester::assert_file_read(const std::vector& state_data, const unsigned char *read_data, FwSignedSizeType size_read) { + // Functional tests + if (functional()) { + ASSERT_EQ(size_read, state_data.size()); + ASSERT_EQ(std::vector(read_data, read_data + size_read), state_data); + FwSignedSizeType position = -1; + FwSignedSizeType shadow_position = -1; + ASSERT_EQ(this->m_file.position(position), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.position(shadow_position), Os::File::Status::OP_OK); + ASSERT_EQ(position, shadow_position); + } +} + +void Os::Test::File::Tester::assert_file_write(const std::vector& write_data, FwSignedSizeType size_written) { + ASSERT_EQ(size_written, write_data.size()); + FwSignedSizeType file_size = 0; + FwSignedSizeType shadow_size = 0; + ASSERT_EQ(this->m_file.size(file_size), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.size(shadow_size), Os::File::Status::OP_OK); + ASSERT_EQ(file_size, shadow_size); + FwSignedSizeType file_position = -1; + FwSignedSizeType shadow_position = -1; + ASSERT_EQ(this->m_file.position(file_position), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.position(shadow_position), Os::File::Status::OP_OK); + ASSERT_EQ(file_position, shadow_position); +} + +void Os::Test::File::Tester::assert_file_seek(const FwSignedSizeType original_position, const FwSignedSizeType seek_desired, const bool absolute) { + FwSignedSizeType new_position = 0; + FwSignedSizeType shadow_position = 0; + + ASSERT_EQ(this->m_file.position(new_position), Os::File::Status::OP_OK); + ASSERT_EQ(this->m_shadow.position(shadow_position), Os::File::Status::OP_OK); + + const FwSignedSizeType expected_offset = (absolute) ? seek_desired : (original_position + seek_desired); + if (expected_offset > 0) { + ASSERT_EQ(new_position, expected_offset); + } else { + ASSERT_EQ(new_position, original_position); + } + ASSERT_EQ(new_position, shadow_position); +} + +// ------------------------------------------------------------------------------------------------------ +// OpenFile: base rule for all open rules +// +// ------------------------------------------------------------------------------------------------------ +Os::Test::File::Tester::OpenBaseRule::OpenBaseRule(const char *rule_name, + Os::File::Mode mode, + const bool overwrite, + const bool randomize_filename) + : STest::Rule(rule_name), m_mode(mode), + m_overwrite(overwrite ? Os::File::OverwriteType::OVERWRITE : Os::File::OverwriteType::NO_OVERWRITE), + m_random(randomize_filename) {} + +bool Os::Test::File::Tester::OpenBaseRule::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return state.m_mode == Os::File::Mode::OPEN_NO_MODE; +} + +void Os::Test::File::Tester::OpenBaseRule::action(Os::Test::File::Tester &state //!< The test state +) { + // Initial variables used for this test + std::shared_ptr filename = state.get_filename(this->m_random); + + // Ensure initial and shadow states synchronized + state.assert_file_consistent(); + state.assert_file_closed(); + + // Perform action and shadow action asserting the results are the same + Os::File::Status status = state.m_file.open(filename->c_str(), m_mode, this->m_overwrite); + ASSERT_EQ(status, state.shadow_open(*filename, m_mode, this->m_overwrite)); + + // Extra check to ensure file is consistently open + if (Os::File::Status::OP_OK == status) { + state.assert_file_opened(*filename, m_mode); + FileState file_state = state.current_file_state(); + ASSERT_EQ(file_state.position, 0); // Open always zeros the position + } + // Assert the file state remains consistent. + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenFileCreate +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenFileCreate::OpenFileCreate(const bool randomize_filename) + : Os::Test::File::Tester::OpenBaseRule("OpenFileCreate", Os::File::Mode::OPEN_CREATE, false, + randomize_filename) {} + + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenFileCreateOverwrite +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenFileCreateOverwrite::OpenFileCreateOverwrite(const bool randomize_filename) + : Os::Test::File::Tester::OpenBaseRule("OpenFileCreate", Os::File::Mode::OPEN_CREATE, true, + randomize_filename) {} + + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenForWrite +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenForWrite::OpenForWrite(const bool randomize_filename) + : Os::Test::File::Tester::OpenBaseRule("OpenForWrite", + // Randomized write mode + static_cast(STest::Pick::lowerUpper( + Os::File::Mode::OPEN_WRITE, + Os::File::Mode::OPEN_APPEND)), + // Randomized overwrite + static_cast(STest::Pick::lowerUpper(0, 1)), + randomize_filename) { + // Ensures that a random write mode will work correctly + static_assert((Os::File::Mode::OPEN_SYNC_WRITE - 1) == Os::File::Mode::OPEN_WRITE, "Write modes not contiguous"); + static_assert((Os::File::Mode::OPEN_APPEND - 1) == Os::File::Mode::OPEN_SYNC_WRITE, + "Write modes not contiguous"); + +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenForRead +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenForRead::OpenForRead(const bool randomize_filename) + : Os::Test::File::Tester::OpenBaseRule("OpenForRead", Os::File::Mode::OPEN_READ, + // Randomized overwrite + static_cast(STest::Pick::lowerUpper(0, 1)), + randomize_filename) {} + +// ------------------------------------------------------------------------------------------------------ +// Rule: CloseFile +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::CloseFile::CloseFile() : STest::Rule("CloseFile") {} + +bool Os::Test::File::Tester::CloseFile::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE != state.m_mode; +} + +void Os::Test::File::Tester::CloseFile::action(Os::Test::File::Tester &state //!< The test state +) { + // Make sure test state and file state synchronized + state.assert_file_consistent(); + state.assert_file_opened(state.m_current_path); + // Close file and shadow state + state.m_file.close(); + state.shadow_close(); + // Assert test state and file state synchronized + state.assert_file_closed(); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::Read::Read() : + STest::Rule("Read") { +} + + +bool Os::Test::File::Tester::Read::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_READ == state.m_mode; +} + + +void Os::Test::File::Tester::Read::action( + Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[FILE_DATA_MAXIMUM]; + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + FwSignedSizeType size_desired = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); + FwSignedSizeType size_read = size_desired; + bool wait = static_cast(STest::Pick::lowerUpper(0, 1)); + Os::File::Status status = state.m_file.read(buffer, size_read, wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT); + ASSERT_EQ(Os::File::Status::OP_OK, status); + std::vector read_data = state.shadow_read(size_desired); + state.assert_file_read(read_data, buffer, size_read); + FileState final_file_state = state.current_file_state(); + // File size should not change during read + ASSERT_EQ(final_file_state.size, original_file_state.size); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Write +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::Write::Write() : + STest::Rule("Write") { +} + + +bool Os::Test::File::Tester::Write::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_CREATE <= state.m_mode; +} + + +void Os::Test::File::Tester::Write::action( + Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[FILE_DATA_MAXIMUM]; + state.assert_file_consistent(); + FwSignedSizeType size_desired = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); + FwSignedSizeType size_written = size_desired; + bool wait = static_cast(STest::Pick::lowerUpper(0, 1)); + for (FwSignedSizeType i = 0; i < size_desired; i++) { + buffer[i] = static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); + } + std::vector write_data(buffer, buffer + size_desired); + Os::File::Status status = state.m_file.write(buffer, size_written, wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT); + ASSERT_EQ(Os::File::Status::OP_OK, status); + ASSERT_EQ(size_written, size_desired); + state.shadow_write(write_data); + state.assert_file_write(write_data, size_written); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::Seek::Seek() : + STest::Rule("Seek") { +} + + +bool Os::Test::File::Tester::Seek::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE < state.m_mode; +} + + +void Os::Test::File::Tester::Seek::action( + Os::Test::File::Tester &state //!< The test state +) { + FwSignedSizeType seek_offset = 0; + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + + // Choose some random values + bool absolute = static_cast(STest::Pick::lowerUpper(0, 1)); + if (absolute) { + seek_offset = STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM); + } else { + seek_offset = STest::Pick::lowerUpper(0, original_file_state.position + FILE_DATA_MAXIMUM) - original_file_state.position; + } + Os::File::Status status = state.m_file.seek(seek_offset, absolute ? Os::File::SeekType::ABSOLUTE : Os::File::SeekType::RELATIVE); + ASSERT_EQ(status, Os::File::Status::OP_OK); + state.shadow_seek(seek_offset, absolute); + state.assert_file_seek(original_file_state.position, seek_offset, absolute); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Preallocate +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::Preallocate::Preallocate() : + STest::Rule("Preallocate") { +} + + +bool Os::Test::File::Tester::Preallocate::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_CREATE <= state.m_mode; +} + + +void Os::Test::File::Tester::Preallocate::action( + Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + FwSignedSizeType offset = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); + FwSignedSizeType length = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); + Os::File::Status status = state.m_file.preallocate(offset, length); + ASSERT_EQ(Os::File::Status::OP_OK, status); + state.shadow_preallocate(offset, length); + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, FW_MAX(original_file_state.size, offset + length)); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Flush +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::Flush::Flush() : + STest::Rule("Flush") { +} + + +bool Os::Test::File::Tester::Flush::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_CREATE <= state.m_mode; +} + + +void Os::Test::File::Tester::Flush::action( + Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + Os::File::Status status = state.m_file.flush(); + ASSERT_EQ(status, Os::File::Status::OP_OK); + state.shadow_flush(); + + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + + + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenInvalidModes::OpenInvalidModes() + : STest::Rule("OpenInvalidModes") {} + +bool Os::Test::File::Tester::OpenInvalidModes::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE != state.m_mode; +} + +void Os::Test::File::Tester::OpenInvalidModes::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + // Check initial file state + state.assert_file_opened(state.m_current_path); + std::shared_ptr filename = state.get_filename(true); + Os::File::Status status = state.m_file.open(filename->c_str(), Os::File::Mode::OPEN_CREATE); + state.assert_valid_mode_status(status); + state.assert_file_opened(state.m_current_path); // Original file remains open + // Ensure no change in size or pointer of original file + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateWithoutOpen +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::PreallocateWithoutOpen::PreallocateWithoutOpen() + : STest::Rule("PreallocateWithoutOpen") {} + +bool Os::Test::File::Tester::PreallocateWithoutOpen::precondition( + const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE == state.m_mode; +} + +void Os::Test::File::Tester::PreallocateWithoutOpen::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + // Check initial file state + state.assert_file_closed(); + // Open file of given filename + FwSignedSizeType random_offset = STest::Pick::lowerUpper(0, std::numeric_limits::max()); + FwSignedSizeType random_size = STest::Pick::lowerUpper(0, std::numeric_limits::max()); + + Os::File::Status status = state.m_file.preallocate(random_offset, random_size); + state.assert_valid_mode_status(status); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekWithoutOpen +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::SeekWithoutOpen::SeekWithoutOpen() : STest::Rule("SeekWithoutOpen") {} + +bool Os::Test::File::Tester::SeekWithoutOpen::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE == state.m_mode; +} + +void Os::Test::File::Tester::SeekWithoutOpen::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + // Check initial file state + state.assert_file_closed(); + // Open file of given filename + FwSignedSizeType random_offset = STest::Pick::lowerUpper(0, std::numeric_limits::max()); + bool random_absolute = static_cast(STest::Pick::lowerUpper(0, 1)); + + Os::File::Status status = state.m_file.seek(random_offset, random_absolute ? Os::File::SeekType::ABSOLUTE : Os::File::SeekType::RELATIVE); + state.assert_valid_mode_status(status); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekInvalidSize +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::SeekInvalidSize::SeekInvalidSize() : STest::Rule("SeekInvalidSize") {} + +bool Os::Test::File::Tester::SeekInvalidSize::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE < state.m_mode;; +} + +void Os::Test::File::Tester::SeekInvalidSize::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + // Open file of given filename + FwSignedSizeType random_offset = STest::Pick::lowerUpper(original_file_state.position + 1, std::numeric_limits::max()); + ASSERT_GT(random_offset, original_file_state.position); + + Os::File::Status status = state.m_file.seek(-1 * random_offset, Os::File::SeekType::RELATIVE); + ASSERT_EQ(Os::File::Status::INVALID_ARGUMENT, status); + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: FlushInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::FlushInvalidModes::FlushInvalidModes() + : STest::Rule("FlushInvalidModes") {} + +bool Os::Test::File::Tester::FlushInvalidModes::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE == state.m_mode || Os::File::Mode::OPEN_READ == state.m_mode; +} + +void Os::Test::File::Tester::FlushInvalidModes::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + ASSERT_TRUE(Os::File::Mode::OPEN_NO_MODE == state.m_file.m_mode || Os::File::Mode::OPEN_READ == state.m_file.m_mode); + Os::File::Status status = state.m_file.flush(); + state.assert_valid_mode_status(status); + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::ReadInvalidModes::ReadInvalidModes() + : STest::Rule("ReadInvalidModes") {} + +bool Os::Test::File::Tester::ReadInvalidModes::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_READ != state.m_mode; +} + +void Os::Test::File::Tester::ReadInvalidModes::action(Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[10]; + FwSignedSizeType size = sizeof buffer; + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + ASSERT_NE(Os::File::Mode::OPEN_READ, state.m_file.m_mode); + bool wait = static_cast(STest::Pick::lowerUpper(0, 1)); + Os::File::Status status = state.m_file.read(buffer, size, wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT); + state.assert_valid_mode_status(status); + + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::WriteInvalidModes::WriteInvalidModes() + : STest::Rule("WriteInvalidModes") {} + +bool Os::Test::File::Tester::WriteInvalidModes::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return Os::File::Mode::OPEN_NO_MODE == state.m_mode || Os::File::Mode::OPEN_READ == state.m_mode; +} + +void Os::Test::File::Tester::WriteInvalidModes::action(Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[10]; + FwSignedSizeType size = sizeof buffer; + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + ASSERT_TRUE(Os::File::Mode::OPEN_NO_MODE == state.m_file.m_mode || Os::File::Mode::OPEN_READ == state.m_file.m_mode); + bool wait = static_cast(STest::Pick::lowerUpper(0, 1)); + Os::File::Status status = state.m_file.write(buffer, size, wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT); + state.assert_valid_mode_status(status); + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Base Rule: AssertRule +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::AssertRule::AssertRule(const char *name) : STest::Rule(name) {} + +bool Os::Test::File::Tester::AssertRule::precondition(const Os::Test::File::Tester &state //!< The test state +) { + return true; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenIllegalPath +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenIllegalPath::OpenIllegalPath() : Os::Test::File::Tester::AssertRule("OpenIllegalPath") {} + +void Os::Test::File::Tester::OpenIllegalPath::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + Os::File::Mode random_mode = + static_cast(STest::Pick::lowerUpper(Os::File::Mode::OPEN_READ, + Os::File::Mode::OPEN_APPEND)); + bool overwrite = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.open(nullptr, random_mode, + overwrite ? Os::File::OverwriteType::OVERWRITE : Os::File::OverwriteType::NO_OVERWRITE), + ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenIllegalMode +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::OpenIllegalMode::OpenIllegalMode() : Os::Test::File::Tester::AssertRule("OpenIllegalMode") {} + +void Os::Test::File::Tester::OpenIllegalMode::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + std::shared_ptr random_filename = state.get_filename(true); + U32 mode = STest::Pick::lowerUpper(0, 1); + bool overwrite = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.open(random_filename->c_str(), + (mode == 0) ? Os::File::Mode::MAX_OPEN_MODE : Os::File::Mode::OPEN_NO_MODE, + overwrite ? Os::File::OverwriteType::OVERWRITE : Os::File::OverwriteType::NO_OVERWRITE), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateIllegalOffset +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::PreallocateIllegalOffset::PreallocateIllegalOffset() + : Os::Test::File::Tester::AssertRule("PreallocateIllegalOffset") {} + +void Os::Test::File::Tester::PreallocateIllegalOffset::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FwSignedSizeType length = static_cast(STest::Pick::any()); + FwSignedSizeType invalid_offset = + -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); + ASSERT_DEATH_IF_SUPPORTED(state.m_file.preallocate(invalid_offset, length), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP) << "With offset: " << invalid_offset; + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateIllegalLength +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::PreallocateIllegalLength::PreallocateIllegalLength() + : Os::Test::File::Tester::AssertRule("PreallocateIllegalLength") {} + +void Os::Test::File::Tester::PreallocateIllegalLength::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FwSignedSizeType offset = static_cast(STest::Pick::any()); + FwSignedSizeType invalid_length = + -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); + ASSERT_DEATH_IF_SUPPORTED(state.m_file.preallocate(offset, invalid_length), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekIllegal +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::SeekIllegal::SeekIllegal() : Os::Test::File::Tester::AssertRule("SeekIllegal") {} + +void Os::Test::File::Tester::SeekIllegal::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + ASSERT_DEATH_IF_SUPPORTED(state.m_file.seek(-1, Os::File::SeekType::ABSOLUTE), Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadIllegalBuffer +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::ReadIllegalBuffer::ReadIllegalBuffer() + : Os::Test::File::Tester::AssertRule("ReadIllegalBuffer") {} + +void Os::Test::File::Tester::ReadIllegalBuffer::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FwSignedSizeType size = static_cast(STest::Pick::any()); + bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.read(nullptr, size, random_wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadIllegalSize +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::ReadIllegalSize::ReadIllegalSize() : Os::Test::File::Tester::AssertRule("ReadIllegalSize") {} + +void Os::Test::File::Tester::ReadIllegalSize::action(Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[10] = {}; + state.assert_file_consistent(); + FwSignedSizeType invalid_size = -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); + bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.read(buffer, invalid_size, + random_wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteIllegalBuffer +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::WriteIllegalBuffer::WriteIllegalBuffer() + : Os::Test::File::Tester::AssertRule("WriteIllegalBuffer") {} + +void Os::Test::File::Tester::WriteIllegalBuffer::action(Os::Test::File::Tester &state //!< The test state +) { + state.assert_file_consistent(); + FwSignedSizeType size = static_cast(STest::Pick::any()); + bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.write(nullptr, size, random_wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteIllegalSize +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::WriteIllegalSize::WriteIllegalSize() : Os::Test::File::Tester::AssertRule("WriteIllegalSize") {} + +void Os::Test::File::Tester::WriteIllegalSize::action(Os::Test::File::Tester &state //!< The test state +) { + U8 buffer[10] = {}; + state.assert_file_consistent(); + FwSignedSizeType invalid_size = -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); + bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); + ASSERT_DEATH_IF_SUPPORTED( + state.m_file.read(buffer, invalid_size, random_wait ? Os::File::WaitType::WAIT : Os::File::WaitType::NO_WAIT), + Os::Test::File::Tester::ASSERT_IN_FILE_CPP); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyAssignment +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::CopyAssignment::CopyAssignment() : + STest::Rule("CopyAssignment") {} + + +bool Os::Test::File::Tester::CopyAssignment::precondition(const Os::Test::File::Tester& state //!< The test state +) { + return true; +} + + +void Os::Test::File::Tester::CopyAssignment::action(Os::Test::File::Tester& state //!< The test state +) { + state.assert_file_consistent(); + Os::File temp = state.m_file; + state.assert_file_consistent(); // Prevents optimization + state.m_file = temp; + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyConstruction +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::CopyConstruction::CopyConstruction() : + STest::Rule("CopyConstruction") {} + + +bool Os::Test::File::Tester::CopyConstruction::precondition(const Os::Test::File::Tester& state //!< The test state +) { + return true; +} + + +void Os::Test::File::Tester::CopyConstruction::action(Os::Test::File::Tester& state //!< The test state +) { + state.assert_file_consistent(); + Os::File temp(state.m_file); + state.assert_file_consistent(); // Interim check to ensure original file did not change + (void) new(&state.m_file)Os::File(temp); // Copy-construct overtop of the original file + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: FullCrc +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::FullCrc::FullCrc() : + STest::Rule("FullCrc") {} + +bool Os::Test::File::Tester::FullCrc::precondition( + const Os::Test::File::Tester& state //!< The test state +) { + return state.m_mode == Os::File::Mode::OPEN_READ; +} + + +void Os::Test::File::Tester::FullCrc::action( + Os::Test::File::Tester& state //!< The test state +) { + U32 crc = 1; + U32 shadow_crc = 2; + state.assert_file_consistent(); + Os::File::Status status = state.m_file.calculateCrc(crc); + state.shadow_crc(shadow_crc); + ASSERT_EQ(status, Os::File::Status::OP_OK); + ASSERT_EQ(crc, shadow_crc); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: IncrementalCrc +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::IncrementalCrc::IncrementalCrc() : + STest::Rule("IncrementalCrc") {} + + +bool Os::Test::File::Tester::IncrementalCrc::precondition( + const Os::Test::File::Tester& state //!< The test state +) { + return state.m_mode == Os::File::Mode::OPEN_READ; +} + + +void Os::Test::File::Tester::IncrementalCrc::action( + Os::Test::File::Tester& state //!< The test state +){ + state.assert_file_consistent(); + FwSignedSizeType size_desired = static_cast(STest::Pick::lowerUpper(0, FW_FILE_CHUNK_SIZE)); + FwSignedSizeType shadow_size = size_desired; + Os::File::Status status = state.m_file.incrementalCrc(size_desired); + state.shadow_partial_crc(shadow_size); + ASSERT_EQ(status, Os::File::Status::OP_OK); + ASSERT_EQ(size_desired, shadow_size); + ASSERT_EQ(state.m_file.m_crc, state.m_independent_crc); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: FinalizeCrc +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::FinalizeCrc::FinalizeCrc() : + STest::Rule("FinalizeCrc") {} + +bool Os::Test::File::Tester::FinalizeCrc::precondition( + const Os::Test::File::Tester& state //!< The test state +) { + return true; +} + +void Os::Test::File::Tester::FinalizeCrc::action( + Os::Test::File::Tester& state //!< The test state +) { + U32 crc = 1; + U32 shadow_crc = 2; + state.assert_file_consistent(); + Os::File::Status status = state.m_file.finalizeCrc(crc); + state.shadow_finalize(shadow_crc); + ASSERT_EQ(status, Os::File::Status::OP_OK); + ASSERT_EQ(crc, shadow_crc); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: FullCrcInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::FullCrcInvalidModes::FullCrcInvalidModes() : + STest::Rule("FullCrcInvalidModes") {} + + +bool Os::Test::File::Tester::FullCrcInvalidModes::precondition( + const Os::Test::File::Tester& state //!< The test state +) { + return Os::File::Mode::OPEN_READ != state.m_mode; +} + +void Os::Test::File::Tester::FullCrcInvalidModes::action( + Os::Test::File::Tester& state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + ASSERT_TRUE(Os::File::Mode::OPEN_READ != state.m_file.m_mode); + U32 crc = 1; + Os::File::Status status = state.m_file.calculateCrc(crc); + ASSERT_EQ(crc, 0); + state.assert_valid_mode_status(status); + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: IncrementalCrcInvalidModes +// +// ------------------------------------------------------------------------------------------------------ + +Os::Test::File::Tester::IncrementalCrcInvalidModes::IncrementalCrcInvalidModes() : + STest::Rule("IncrementalCrcInvalidModes") {} + + +bool Os::Test::File::Tester::IncrementalCrcInvalidModes::precondition( + const Os::Test::File::Tester& state //!< The test state +) { + return Os::File::Mode::OPEN_READ != state.m_mode;; +} + + +void Os::Test::File::Tester::IncrementalCrcInvalidModes::action( + Os::Test::File::Tester& state //!< The test state +) { + state.assert_file_consistent(); + FileState original_file_state = state.current_file_state(); + ASSERT_TRUE(Os::File::Mode::OPEN_READ != state.m_file.m_mode); + FwSignedSizeType size = static_cast(STest::Pick::lowerUpper(0, 1)); + Os::File::Status status = state.m_file.incrementalCrc(size); + state.assert_valid_mode_status(status); + // Ensure no change in size or pointer + FileState final_file_state = state.current_file_state(); + ASSERT_EQ(final_file_state.size, original_file_state.size); + ASSERT_EQ(final_file_state.position, original_file_state.position); + state.assert_file_consistent(); +} + diff --git a/Os/test/ut/file/FileRules.hpp b/Os/test/ut/file/FileRules.hpp new file mode 100644 index 0000000000..a0e5e2e0a6 --- /dev/null +++ b/Os/test/ut/file/FileRules.hpp @@ -0,0 +1,849 @@ +// ====================================================================== +// \title Os/test/ut/file/MyRules.hpp +// \brief rule definitions for common testing +// ====================================================================== +// Stripped when compiled, here for IDEs +#include "RulesHeaders.hpp" + +// ------------------------------------------------------------------------------------------------------ +// OpenFile: base rule for all open rules +// +// ------------------------------------------------------------------------------------------------------ +struct OpenBaseRule : public STest::Rule { + //! Constructor + OpenBaseRule(const char *rule_name, Os::File::Mode mode = Os::File::Mode::OPEN_CREATE, const bool overwrite = false, + const bool randomize_filename = false); + + Os::File::Mode m_mode; + Os::File::OverwriteType m_overwrite; + bool m_random; + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenFileCreate +// +// ------------------------------------------------------------------------------------------------------ +struct OpenFileCreate : public OpenBaseRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + explicit OpenFileCreate(const bool randomize_filename = false); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenFileCreateOverwrite +// +// ------------------------------------------------------------------------------------------------------ +struct OpenFileCreateOverwrite : public OpenBaseRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + explicit OpenFileCreateOverwrite(const bool randomize_filename = false); +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenForWrite +// +// ------------------------------------------------------------------------------------------------------ +struct OpenForWrite : public OpenBaseRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + explicit OpenForWrite(const bool randomize_filename = false); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenForRead +// +// ------------------------------------------------------------------------------------------------------ +struct OpenForRead : public OpenBaseRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + explicit OpenForRead(const bool randomize_filename = false); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: CloseFile +// +// ------------------------------------------------------------------------------------------------------ +struct CloseFile : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + CloseFile(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read +// +// ------------------------------------------------------------------------------------------------------ +struct Read : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + Read(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester &state //!< The test state + ); + +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: Write +// +// ------------------------------------------------------------------------------------------------------ +struct Write : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + Write(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester &state //!< The test state + ); + +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: Seek +// +// ------------------------------------------------------------------------------------------------------ +struct Seek : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + Seek(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester &state //!< The test state + ); + +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Preallocate +// +// ------------------------------------------------------------------------------------------------------ +struct Preallocate : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + Preallocate(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester &state //!< The test state + ); + +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: Flush +// +// ------------------------------------------------------------------------------------------------------ +struct Flush : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + Flush(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester &state //!< The test state + ); + +}; + + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct OpenInvalidModes : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + OpenInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateWithoutOpen +// +// ------------------------------------------------------------------------------------------------------ +struct PreallocateWithoutOpen : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + PreallocateWithoutOpen(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekWithoutOpen +// +// ------------------------------------------------------------------------------------------------------ +struct SeekWithoutOpen : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + SeekWithoutOpen(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekInvalidSize +// +// ------------------------------------------------------------------------------------------------------ +struct SeekInvalidSize : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + SeekInvalidSize(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: FlushInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct FlushInvalidModes : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + FlushInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct ReadInvalidModes : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + ReadInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct WriteInvalidModes : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + WriteInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Base Rule: AssertRule +// +// ------------------------------------------------------------------------------------------------------ +struct AssertRule : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + explicit AssertRule(const char *name); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::File::Tester &state //!< The test state + ); + + //! Action + virtual void action(Os::Test::File::Tester &state //!< The test state + ) = 0; +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenIllegalPath +// +// ------------------------------------------------------------------------------------------------------ +struct OpenIllegalPath : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + OpenIllegalPath(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ) override; +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenIllegalMode +// +// ------------------------------------------------------------------------------------------------------ +struct OpenIllegalMode : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + OpenIllegalMode(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateIllegalOffset +// +// ------------------------------------------------------------------------------------------------------ +struct PreallocateIllegalOffset : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + PreallocateIllegalOffset(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: PreallocateIllegalLength +// +// ------------------------------------------------------------------------------------------------------ +struct PreallocateIllegalLength : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + PreallocateIllegalLength(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: SeekIllegal +// +// ------------------------------------------------------------------------------------------------------ +struct SeekIllegal : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + SeekIllegal(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadIllegalBuffer +// +// ------------------------------------------------------------------------------------------------------ +struct ReadIllegalBuffer : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + ReadIllegalBuffer(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadIllegalSize +// +// ------------------------------------------------------------------------------------------------------ +struct ReadIllegalSize : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + ReadIllegalSize(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteIllegalBuffer +// +// ------------------------------------------------------------------------------------------------------ +struct WriteIllegalBuffer : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + WriteIllegalBuffer(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: WriteIllegalSize +// +// ------------------------------------------------------------------------------------------------------ +struct WriteIllegalSize : public AssertRule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + WriteIllegalSize(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Action + void action(Os::Test::File::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyAssignment +// +// ------------------------------------------------------------------------------------------------------ +struct CopyAssignment : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + CopyAssignment(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); + +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyConstruction +// +// ------------------------------------------------------------------------------------------------------ +struct CopyConstruction : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + CopyConstruction(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); + +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: FullCrc +// +// ------------------------------------------------------------------------------------------------------ +struct FullCrc : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + FullCrc(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: IncrementalCrc +// +// ------------------------------------------------------------------------------------------------------ +struct IncrementalCrc : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + IncrementalCrc(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); + +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: FinalizeCrc +// +// ------------------------------------------------------------------------------------------------------ +struct FinalizeCrc : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + FinalizeCrc(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); + +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: FullCrcInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct FullCrcInvalidModes : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + FullCrcInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: IncrementalCrcInvalidModes +// +// ------------------------------------------------------------------------------------------------------ +struct IncrementalCrcInvalidModes : public STest::Rule { + + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + IncrementalCrcInvalidModes(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition( + const Os::Test::File::Tester& state //!< The test state + ); + + //! Action + void action( + Os::Test::File::Tester& state //!< The test state + ); +}; diff --git a/Os/test/ut/file/RulesHeaders.hpp b/Os/test/ut/file/RulesHeaders.hpp new file mode 100644 index 0000000000..f280c19f0b --- /dev/null +++ b/Os/test/ut/file/RulesHeaders.hpp @@ -0,0 +1,160 @@ +// ====================================================================== +// \title Os/test/ut/file/RulesHeaders.hpp +// \brief rule definitions for common testing +// ====================================================================== + +#ifndef __RULES_HEADERS__ +#define __RULES_HEADERS__ +#include +#include +#include +#include "Os/File.hpp" +#include "STest/Rule/Rule.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" +#include "STest/Scenario/Scenario.hpp" +#include "Os/test/ut/file/SyntheticFileSystem.hpp" + +namespace Os { +namespace Test { +namespace File { + +struct Tester { + //! State data for an open OS file. + //! + struct FileState { + FwSignedSizeType size = -1; + FwSignedSizeType position = -1; + }; + + //! Assert in File.cpp for searching death text + static constexpr const char* ASSERT_IN_FILE_CPP = "Assert: \".*/Os/File\\.cpp:[0-9]+\""; + + // Constructors that ensures the file is always valid + Tester(); + + // Destructor must be virtual + virtual ~Tester() = default; + + //! Check if the test file exists. + //! \return true if it exists, false otherwise. + //! + virtual bool exists(const std::string& filename) const = 0; + + //! Check if the back-end tester is fully functional + //! \return true if functional, false otherwise + //! + virtual bool functional() const = 0; + + //! Get a filename, randomly if random is true, otherwise use a basic filename. + //! \param random: true if filename should be random, false if predictable + //! \return: filename to use for testing as a shared pointer + //! + virtual std::shared_ptr get_filename(bool random) const = 0; + + //! Performs the "open" action on the shadow state. + //! + Os::File::Status shadow_open(const std::string &path, Os::File::Mode newly_opened_mode = Os::File::Mode::OPEN_NO_MODE, + bool overwrite = false); + + //! Perform the "close" action on the shadow state. + //! + void shadow_close(); + + //! Perform the "read" action on the shadow state returning the read data. + //! \return data read + //! + std::vector shadow_read(FwSignedSizeType size); + + //! Perform the "write" action on the shadow state given the data. + //! + void shadow_write(const std::vector& data); + + //! Perform the "seek" action on the shadow state given. + //! + void shadow_seek(const FwSignedSizeType offset, const bool absolute); + + //! Perform the "preallocate" action on the shadow state. + //! + void shadow_preallocate(const FwSignedSizeType offset, const FwSignedSizeType length); + + //! Perform the "flush" action on the shadow state. + //! + void shadow_flush(); + + //! Perform the "full crc" action on the shadow state. + //! \param crc: output for CRC value + //! + void shadow_crc(U32& crc); + + //! Perform the "incremental crc" action on the shadow state. + //! \param crc: output for CRC value + //! + void shadow_partial_crc(FwSignedSizeType& size); + + //! Perform the "crc finalize" action on the shadow state. + //! \param crc: output for CRC value + //! + void shadow_finalize(U32& crc); + + //! Detect current state of the file. Note: for files that are known, but unopened this will open the file as read + //! detect the state, and then close it again. + //! \return file state for the test + //! + FileState current_file_state(); + + //! Assert file and shadow state are in-sync + //! + void assert_file_consistent(); + + //! Assert that the supplied status is appropriate for the current mode. + //! \param status: status to check + //! + void assert_valid_mode_status(Os::File::Status& status) const; + + //! Assert that the file is opened + //! + void assert_file_opened(const std::string& path="", Os::File::Mode newly_opened_mode = Os::File::Mode::OPEN_NO_MODE, bool overwrite = false); + + //! Assert that the file is closed + //! + void assert_file_closed(); + + //! Assert a file read + //! + void assert_file_read(const std::vector& state_data, const unsigned char* read_data, FwSignedSizeType size_read); + + //! Assert a file write + //! + void assert_file_write(const std::vector& write_data, FwSignedSizeType size_written); + + //! Assert a file seek + //! + void assert_file_seek(const FwSignedSizeType original_position, const FwSignedSizeType seek_desired, const bool absolute); + + //! File under test + Os::File m_file; + + //! Shadow file state: file system + Os::Test::SyntheticFile m_shadow; + + //! Currently opened path + std::string m_current_path; + + //! Independent tracking of mode + Os::File::Mode m_mode = Os::File::Mode::OPEN_NO_MODE; + + U32 m_independent_crc = Os::File::INITIAL_CRC; + +// Do NOT alter, adds rules to Tester as inner classes +#include "FileRules.hpp" +}; + +//! Get the tester implementation for the given backend. +//! \return pointer to tester subclass implementation +//! +std::unique_ptr get_tester_implementation(); +} // namespace File +} // namespace Test +} // namespace Os +#endif // __RULES_HEADERS__ diff --git a/Os/test/ut/file/SyntheticFileSystem.cpp b/Os/test/ut/file/SyntheticFileSystem.cpp new file mode 100644 index 0000000000..171b3fbb50 --- /dev/null +++ b/Os/test/ut/file/SyntheticFileSystem.cpp @@ -0,0 +1,258 @@ +// ====================================================================== +// \title Os/test/ut/file/SyntheticFileSystem.cpp +// \brief standard template library driven synthetic file system implementation +// ====================================================================== +#include "Os/test/ut/file/SyntheticFileSystem.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Os { +namespace Test { + +SyntheticFileSystem::OpenData SyntheticFileSystem::open(const CHAR* char_path, const Os::File::Mode open_mode, const File::OverwriteType overwrite) { + SyntheticFileSystem::OpenData return_value; + std::string path = char_path; + bool exists = (this->m_filesystem.count(path) > 0); + + // Error case: read on file that does not exist + if ((not exists) && (Os::File::Mode::OPEN_READ == open_mode)) { + return_value.status = Os::File::Status::DOESNT_EXIST; + } + // Error case: create on existing file without overwrite + else if (exists and (not overwrite) && (open_mode == Os::File::Mode::OPEN_CREATE)) { + return_value.status = Os::File::Status::FILE_EXISTS; + } + // Case where file should be opened correctly + else { + const bool truncate = (Os::File::Mode::OPEN_CREATE == open_mode) && overwrite; + + // Add new shadow file data when the file is new + if (not exists) { + this->m_filesystem[path] = std::make_shared(); + } + return_value.file = this->m_filesystem[path]; + return_value.status = Os::File::Status::OP_OK; + + // Set file properties + if (truncate) { + return_value.file->m_data.clear(); + } + return_value.file->m_pointer = 0; + return_value.file->m_mode = open_mode; + return_value.file->m_path = path; + // Checks on the shadow data to ensure consistency + FW_ASSERT(return_value.file->m_mode != Os::File::OPEN_NO_MODE); + FW_ASSERT(return_value.file->m_pointer == 0); + FW_ASSERT(return_value.file->m_path == path); + FW_ASSERT(not truncate || return_value.file->m_data.empty()); + } + return return_value; +} + +bool SyntheticFileSystem::exists(const CHAR* char_path) { + std::string path = char_path; + FW_ASSERT(this->m_filesystem.count(path) <= 1); + return this->m_filesystem.count(path) == 1; +} + +void SyntheticFileSystem::remove(const CHAR* char_path) { + std::string path = char_path; + this->m_filesystem.erase(path); +} + +std::unique_ptr SyntheticFile::s_file_system = std::unique_ptr(new SyntheticFileSystem()); + +void SyntheticFile::setFileSystem(std::unique_ptr new_file_system) { + s_file_system = std::move(new_file_system); +} + +void SyntheticFile::remove(const CHAR* char_path) { + FW_ASSERT(s_file_system != nullptr); + s_file_system->remove(char_path); +} + +File::Status SyntheticFile::open(const CHAR* char_path, const Os::File::Mode open_mode, const File::OverwriteType overwrite) { + SyntheticFileSystem::OpenData data = s_file_system->open(char_path, open_mode, overwrite); + if (data.status == Os::File::Status::OP_OK) { + this->m_data = data.file; + FW_ASSERT(this->m_data != nullptr); + } + return data.status; +} + +void SyntheticFile::close() { + if (this->m_data != nullptr) { + this->m_data->m_mode = Os::File::Mode::OPEN_NO_MODE; + this->m_data->m_path.clear(); + // Checks on the shadow data to ensure consistency + FW_ASSERT(this->m_data->m_mode == Os::File::Mode::OPEN_NO_MODE); + FW_ASSERT(this->m_data->m_path.empty()); + } +} + +Os::File::Status SyntheticFile::read(U8* buffer, FwSignedSizeType& size, WaitType wait) { + (void) wait; + FW_ASSERT(this->m_data != nullptr); + FW_ASSERT(buffer != nullptr); + FW_ASSERT(size >= 0); + FW_ASSERT(this->m_data->m_mode < Os::File::Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (Os::File::Mode::OPEN_NO_MODE == this->m_data->m_mode) { + size = 0; + return Os::File::Status::NOT_OPENED; + } else if (Os::File::Mode::OPEN_READ != this->m_data->m_mode) { + size = 0; + return Os::File::Status::INVALID_MODE; + } + std::vector output; + FwSignedSizeType original_pointer = this->m_data->m_pointer; + FwSignedSizeType original_size = this->m_data->m_data.size(); + // Check expected read bytes + FwSignedSizeType i = 0; + for (i = 0; i < size; i++, this->m_data->m_pointer++) { + // End of file + if (this->m_data->m_pointer >= static_cast(this->m_data->m_data.size())) { + break; + } + buffer[i] = this->m_data->m_data.at(this->m_data->m_pointer); + } + size = i; + // Checks on the shadow data to ensure consistency + FW_ASSERT(this->m_data->m_data.size() == static_cast(original_size)); + FW_ASSERT(this->m_data->m_pointer == ((original_pointer > original_size) ? original_pointer : FW_MIN(original_pointer + size, original_size))); + FW_ASSERT(size == ((original_pointer > original_size) ? 0 : FW_MIN(size, original_size - original_pointer))); + return Os::File::Status::OP_OK; +} + +Os::File::Status SyntheticFile::write(const U8* buffer, FwSignedSizeType& size, WaitType wait) { + (void) wait; + FW_ASSERT(this->m_data != nullptr); + FW_ASSERT(buffer != nullptr); + FW_ASSERT(size >= 0); + FW_ASSERT(this->m_data->m_mode < Os::File::Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (Os::File::Mode::OPEN_NO_MODE == this->m_data->m_mode) { + size = 0; + return Os::File::Status::NOT_OPENED; + } else if (Os::File::Mode::OPEN_READ == this->m_data->m_mode) { + size = 0; + return Os::File::Status::INVALID_MODE; + } + FwSignedSizeType original_position = this->m_data->m_pointer; + FwSignedSizeType original_size = this->m_data->m_data.size(); + const U8* write_data = reinterpret_cast(buffer); + + // Appends seek to end before writing + if (Os::File::Mode::OPEN_APPEND == this->m_data->m_mode) { + this->m_data->m_pointer = this->m_data->m_data.size(); + } + + // First add in zeros to account for a pointer past the end of the file + const FwSignedSizeType zeros = static_cast(this->m_data->m_pointer) - static_cast(this->m_data->m_data.size()); + for (FwSignedSizeType i = 0; i < zeros; i++) { + this->m_data->m_data.push_back(0); + } + // Interim checks to ensure zeroing performed correctly + FW_ASSERT(static_cast(this->m_data->m_pointer) <= this->m_data->m_data.size()); + FW_ASSERT(this->m_data->m_data.size() == + static_cast((Os::File::Mode::OPEN_APPEND == this->m_data->m_mode) ? original_size : FW_MAX(original_position, original_size))); + + FwSignedSizeType pre_write_position = this->m_data->m_pointer; + FwSignedSizeType pre_write_size = this->m_data->m_data.size(); + + // Next write data + FwSignedSizeType i = 0; + for (i = 0; i < size; i++, this->m_data->m_pointer++) { + // Overwrite case + if (static_cast(this->m_data->m_pointer) < this->m_data->m_data.size()) { + this->m_data->m_data.at(this->m_data->m_pointer) = write_data[i]; + } + // Append case + else { + this->m_data->m_data.push_back(write_data[i]); + } + } + size = i; + // Checks on the shadow data to ensure consistency + FW_ASSERT(this->m_data->m_data.size() == + static_cast(FW_MAX(pre_write_position + size, pre_write_size))); + FW_ASSERT(this->m_data->m_pointer == + ((Os::File::Mode::OPEN_APPEND == this->m_data->m_mode) ? pre_write_size : pre_write_position) + size); + return Os::File::Status::OP_OK; +} + +Os::File::Status SyntheticFile::seek(const FwSignedSizeType offset, const SeekType absolute) { + FW_ASSERT(this->m_data != nullptr); + Os::File::Status status = Os::File::Status::OP_OK; + // Cannot do a seek with a negative offset in absolute mode + FW_ASSERT(not absolute || offset >= 0); + FW_ASSERT(this->m_data->m_mode < Os::File::Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (Os::File::Mode::OPEN_NO_MODE == this->m_data->m_mode) { + status = Os::File::Status::NOT_OPENED; + } else { + FwSignedSizeType new_offset = (absolute) ? offset : (offset + this->m_data->m_pointer); + if (new_offset > 0) { + this->m_data->m_pointer = new_offset; + } else { + status = Os::File::Status::INVALID_ARGUMENT; + } + } + return status; +} + +Os::File::Status SyntheticFile::preallocate(const FwSignedSizeType offset, const FwSignedSizeType length) { + FW_ASSERT(this->m_data != nullptr); + Os::File::Status status = Os::File::Status::OP_OK; + FW_ASSERT(offset >= 0); + FW_ASSERT(length >= 0); + FW_ASSERT(this->m_data->m_mode < Os::File::Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (Os::File::Mode::OPEN_NO_MODE == this->m_data->m_mode) { + status = Os::File::Status::NOT_OPENED; + } else if (Os::File::Mode::OPEN_READ == this->m_data->m_mode) { + status = Os::File::Status::INVALID_MODE; + } else { + const FwSignedSizeType original_size = this->m_data->m_data.size(); + const FwSignedSizeType new_length = offset + length; + // Loop from existing size to new size adding zeros + for (FwSignedSizeType i = static_cast(this->m_data->m_data.size()); i < new_length; i++) { + this->m_data->m_data.push_back(0); + } + FW_ASSERT(this->m_data->m_data.size() == static_cast(FW_MAX(offset + length, original_size))); + } + return status; +} + +Os::File::Status SyntheticFile::flush() { + FW_ASSERT(this->m_data != nullptr); + Os::File::Status status = Os::File::Status::OP_OK; + FW_ASSERT(this->m_data->m_mode < Os::File::Mode::MAX_OPEN_MODE); + // Check that the file is open before attempting operation + if (Os::File::Mode::OPEN_NO_MODE == this->m_data->m_mode) { + status = Os::File::Status::NOT_OPENED; + } else if (Os::File::Mode::OPEN_READ == this->m_data->m_mode) { + status = Os::File::Status::INVALID_MODE; + } + return status; +} + +Os::File::Status SyntheticFile::position(FwSignedSizeType &position) { + position = this->m_data->m_pointer; + return Os::File::OP_OK; +} + +Os::File::Status SyntheticFile::size(FwSignedSizeType &size) { + size = static_cast(this->m_data->m_data.size()); + return Os::File::OP_OK; +} + +FileHandle* SyntheticFile::getHandle() { + return this->m_data.get(); +} + +bool SyntheticFile::exists(const CHAR* path) { + return s_file_system->exists(path); +} + +} // namespace Test +} // namespace Os diff --git a/Os/test/ut/file/SyntheticFileSystem.hpp b/Os/test/ut/file/SyntheticFileSystem.hpp new file mode 100644 index 0000000000..968101bc35 --- /dev/null +++ b/Os/test/ut/file/SyntheticFileSystem.hpp @@ -0,0 +1,176 @@ +// ====================================================================== +// \title Os/test/ut/file/SyntheticFileSystem.hpp +// \brief standard template library driven synthetic file system definitions +// ====================================================================== +#include "config/FpConfig.h" +#include "Os/File.hpp" +#include +#include +#include +#include + +#ifndef OS_TEST_UT_FILE_SYNTHETIC_FILE_SYSTEM +#define OS_TEST_UT_FILE_SYNTHETIC_FILE_SYSTEM + +namespace Os { +namespace Test { +// Forward declaration +class SyntheticFileSystem; + +struct SyntheticFileData : public FileHandle { + //! Path of this file + std::string m_path; + //! Data stored in the file + std::vector m_data; + //! Pointer of the file + FwSignedSizeType m_pointer = -1; + //! Separate mode tracking + File::Mode m_mode = File::OPEN_NO_MODE; +}; + +//! \brief Synthetic file data implementation +//! +//! File implementation for a synthetic standard template library based file. +//! +class SyntheticFile : public FileInterface { + public: + friend class SyntheticFileSystem; + + //! \brief check if file exists + static bool exists(const CHAR* path); + + //! \brief set the file system + static void setFileSystem(std::unique_ptr new_file_system); + + //! \brief remove a file by path + static void remove(const CHAR* path); + + //! \brief open a given path with mode and overwrite + //! + //! This opens a file at the given path. Mode drives the mode of the file and overwrite will overwrite files if it + //! exists and was created. Returns a pair of status and synthetic file data. + //! + //! \param path: path to open + //! \param mode: mode to open with + //! \param overwrite: overwrite if exists + //! \return (status, synthetic file object) + //! + Status open(const CHAR *path, const Os::File::Mode mode, const OverwriteType overwrite) override; + + //! \brief close the file + //! + void close() override; + + //! \brief read data from the file + //! + //! Read from the synthetic file and fill the buffer up-to size. Fill size with the data that was read. + //! + //! \param buffer: buffer to fill + //! \param size: size of data to read + //! \param wait: wait, unused + //! \return status of the read + //! + Os::File::Status read(U8 *buffer, FwSignedSizeType &size, File::WaitType wait) override; + + //! \brief write data to the file + //! + //! Write to the synthetic file from the buffer of given size. Fill size with the data that was written. + //! + //! \param buffer: buffer to fill + //! \param size: size of data to read + //! \param bool: wait, unused + //! \return status of the write + //! + Os::File::Status write(const U8 *buffer, FwSignedSizeType &size, File::WaitType wait) override; + + //! \brief seek pointer within file + //! + //! Seek the pointer within the file. + //! + //! \param offset: offset to seek to + //! \param bool: absolute + //! \return status of the seek + //! + Os::File::Status seek(const FwSignedSizeType offset, const File::SeekType absolute) override; + + //! \brief preallocate data within file + //! + //! Preallocate data within the file. + //! + //! \param offset: offset to start pre-allocation + //! \param length: length of the pre-allocation + //! \return status of the preallocate + //! + Os::File::Status preallocate(const FwSignedSizeType offset, const FwSignedSizeType length) override; + + //! \brief flush is no-op + //! + Os::File::Status flush() override; + + //! \brief pointer getter + Os::File::Status position(FwSignedSizeType& position) override; + + //! \brief size getter + Os::File::Status size(FwSignedSizeType& size) override; + + //! \brief silt data handle + FileHandle* getHandle() override; + + std::shared_ptr m_data; + + static std::unique_ptr s_file_system; +}; + + +//! \brief Synthetic file system implementation +//! +//! A synthetic standard template library based in-memory file system for use with testing. It is composed of a map of string paths to a +//! synthetic file data packet that tracks data and file pointer +//! +class SyntheticFileSystem { + public: + friend class SyntheticFile; + //! \brief data returned by the open call + //! + struct OpenData { + std::shared_ptr file; + Os::File::Status status = Os::File::Status::OTHER_ERROR; + }; + + //! Constructor + SyntheticFileSystem() = default; + + //! Destructor + virtual ~SyntheticFileSystem() = default; + + //! \brief check a file exists + //! + //! Check if a file exists. + //! + //! \return: true if exists, false otherwise + //! + bool exists(const CHAR *path); + + //! \brief remove file + void remove(const CHAR* path); + + private: + //! \brief open a given path with mode and overwrite + //! + //! This opens a file at the given path. Mode drives the mode of the file and overwrite will overwrite files if it + //! exists and was created. Returns a pair of status and synthetic file data. + //! + //! \param path: path to open + //! \param mode: mode to open with + //! \param overwrite: overwrite if exists + //! \return (status, synthetic file object) + //! + OpenData open(const CHAR *path, const Os::File::Mode mode, const File::OverwriteType overwrite); + //! Shadow file state: file system + std::map> m_filesystem; +}; + +} // namespace Test +} // namespace Os + +#endif // OS_TEST_UT_FILE_SYNTHETIC_FILE_SYSTEM diff --git a/Svc/ActiveLogger/CMakeLists.txt b/Svc/ActiveLogger/CMakeLists.txt index b31e2a3103..185bacfcf9 100644 --- a/Svc/ActiveLogger/CMakeLists.txt +++ b/Svc/ActiveLogger/CMakeLists.txt @@ -13,9 +13,6 @@ set(SOURCE_FILES register_fprime_module() ### UTs ### -set(UT_MOD_DEPS - Os_Stubs -) set(UT_SOURCE_FILES "${FPRIME_FRAMEWORK_PATH}/Svc/ActiveLogger/ActiveLogger.fpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ActiveLoggerTester.cpp" diff --git a/Svc/ActiveLogger/test/ut/ActiveLoggerImplTester.cpp b/Svc/ActiveLogger/test/ut/ActiveLoggerImplTester.cpp index 0f09b1de9a..89f726b607 100644 --- a/Svc/ActiveLogger/test/ut/ActiveLoggerImplTester.cpp +++ b/Svc/ActiveLogger/test/ut/ActiveLoggerImplTester.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include @@ -583,15 +582,15 @@ namespace Svc { // first read should be delimiter BYTE de; - NATIVE_INT_TYPE readSize = sizeof(de); + FwSignedSizeType readSize = sizeof(de); - ASSERT_EQ(file.read(&de,readSize,true),Os::File::OP_OK); + ASSERT_EQ(file.read(&de,readSize,Os::File::WaitType::WAIT),Os::File::OP_OK); ASSERT_EQ(delimiter,de); // next is LogPacket Fw::ComBuffer comBuff; // size is specific to this test readSize = sizeof(FwPacketDescriptorType) + sizeof(FwEventIdType) + Fw::Time::SERIALIZED_SIZE + sizeof(U32); - ASSERT_EQ(file.read(comBuff.getBuffAddr(),readSize,true),Os::File::OP_OK); + ASSERT_EQ(file.read(comBuff.getBuffAddr(),readSize,Os::File::WaitType::WAIT),Os::File::OP_OK); comBuff.setBuffLen(readSize); // deserialize LogPacket diff --git a/Svc/ActiveTextLogger/CMakeLists.txt b/Svc/ActiveTextLogger/CMakeLists.txt index 5196f3235b..c0f372cf8d 100644 --- a/Svc/ActiveTextLogger/CMakeLists.txt +++ b/Svc/ActiveTextLogger/CMakeLists.txt @@ -14,9 +14,6 @@ set(SOURCE_FILES register_fprime_module() ### UTs ### -set(UT_MOD_DEPS - Os_Stubs -) set(UT_SOURCE_FILES "${FPRIME_FRAMEWORK_PATH}/Svc/ActiveTextLogger/ActiveTextLogger.fpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ActiveTextLoggerTester.cpp" diff --git a/Svc/ActiveTextLogger/LogFile.cpp b/Svc/ActiveTextLogger/LogFile.cpp index 4b95efd00b..cb4cfb34b2 100644 --- a/Svc/ActiveTextLogger/LogFile.cpp +++ b/Svc/ActiveTextLogger/LogFile.cpp @@ -61,8 +61,8 @@ namespace Svc { // Won't exceed max size, so write to file: else { - NATIVE_INT_TYPE writeSize = static_cast(size); - Os::File::Status stat = this->m_file.write(buf,writeSize,true); + FwSignedSizeType writeSize = size; + Os::File::Status stat = this->m_file.write(reinterpret_cast(buf),writeSize,Os::File::WAIT); // Assert that we are not trying to write to a file we never opened: FW_ASSERT(stat != Os::File::NOT_OPENED); @@ -95,7 +95,7 @@ namespace Svc { } U32 suffix = 0; - FwSizeType tmp; + FwSignedSizeType tmp; char fileNameFinal[Fw::String::STRING_SIZE]; (void) strncpy(fileNameFinal,fileName, Fw::String::STRING_SIZE); @@ -140,7 +140,7 @@ namespace Svc { } // Open the file (using CREATE so that it truncates an already existing file): - Os::File::Status stat = this->m_file.open(fileNameFinal, Os::File::OPEN_CREATE, false); + Os::File::Status stat = this->m_file.open(fileNameFinal, Os::File::OPEN_CREATE, Os::File::OverwriteType::NO_OVERWRITE); // Bad status when trying to open the file: if (stat != Os::File::OP_OK) { diff --git a/Svc/ActiveTextLogger/test/ut/ActiveTextLoggerTester.cpp b/Svc/ActiveTextLogger/test/ut/ActiveTextLoggerTester.cpp index 948cfa57f3..e0fbab8b7c 100644 --- a/Svc/ActiveTextLogger/test/ut/ActiveTextLoggerTester.cpp +++ b/Svc/ActiveTextLogger/test/ut/ActiveTextLoggerTester.cpp @@ -125,7 +125,7 @@ namespace Svc { ASSERT_EQ(512U, this->component.m_log_file.m_maxFileSize); // Test predicted size matches actual: - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; Os::FileSystem::getFileSize("test_file",fileSize); ASSERT_EQ(fileSize,this->component.m_log_file.m_currentFileSize); @@ -164,7 +164,7 @@ namespace Svc { { // TODO file errors- use the Os/Stubs? - FwSizeType tmp; + FwSignedSizeType tmp; printf("Testing writing text that is larger than FW_INTERNAL_INTERFACE_STRING_MAX_SIZE\n"); // Can't test this b/c max size of TextLogString is 256 and diff --git a/Svc/BufferLogger/BufferLoggerFile.cpp b/Svc/BufferLogger/BufferLoggerFile.cpp index c94bd68eb5..2a5c290651 100644 --- a/Svc/BufferLogger/BufferLoggerFile.cpp +++ b/Svc/BufferLogger/BufferLoggerFile.cpp @@ -202,8 +202,8 @@ namespace Svc { ) { FW_ASSERT(length > 0, length); - NATIVE_INT_TYPE size = length; - const Os::File::Status fileStatus = this->m_osFile.write(data, size); + FwSignedSizeType size = length; + const Os::File::Status fileStatus = this->m_osFile.write(reinterpret_cast(data), size); bool status; if (fileStatus == Os::File::OP_OK && size == static_cast(length)) { this->m_bytesWritten += length; diff --git a/Svc/BufferLogger/test/ut/BufferLoggerTester.cpp b/Svc/BufferLogger/test/ut/BufferLoggerTester.cpp index c457559d57..9c6306f410 100644 --- a/Svc/BufferLogger/test/ut/BufferLoggerTester.cpp +++ b/Svc/BufferLogger/test/ut/BufferLoggerTester.cpp @@ -265,7 +265,7 @@ namespace Svc { { // Make sure the file size is within bounds - FwSizeType actualSize = 0; + FwSignedSizeType actualSize = 0; const Os::FileSystem::Status status = Os::FileSystem::getFileSize(fileName, actualSize); ASSERT_EQ(Os::FileSystem::OP_OK, status); @@ -284,8 +284,8 @@ namespace Svc { U8 buf[expectedSize]; for (U32 i = 0; i < expectedNumBuffers; ++i) { // Get length of buffer to read - NATIVE_INT_TYPE length = sizeof(SIZE_TYPE); - Os::File::Status status = file.read(&buf, length); + FwSignedSizeType length = sizeof(SIZE_TYPE); + Os::File::Status status = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK, status); ASSERT_EQ(sizeof(SIZE_TYPE), static_cast(length)); Fw::SerialBuffer comBuffLength(buf, length); @@ -297,7 +297,7 @@ namespace Svc { ASSERT_EQ(sizeof(data), bufferSize); // Read and check the buffer length = bufferSize; - status = file.read(&buf, length); + status = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK, status); ASSERT_EQ(bufferSize, static_cast(length)); ASSERT_EQ(memcmp(buf, data, sizeof(data)), 0); @@ -305,8 +305,8 @@ namespace Svc { // Make sure we reached the end of the file { - NATIVE_INT_TYPE length = 10; - const Os::File::Status status = file.read(&buf, length); + FwSignedSizeType length = 10; + const Os::File::Status status = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK, status); ASSERT_EQ(0, length); } diff --git a/Svc/CmdSequencer/CMakeLists.txt b/Svc/CmdSequencer/CMakeLists.txt index 761246eaae..6573dff0b0 100644 --- a/Svc/CmdSequencer/CMakeLists.txt +++ b/Svc/CmdSequencer/CMakeLists.txt @@ -59,5 +59,9 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/CmdSequencerMain.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/CmdSequencerTester.cpp" ) -set(UT_MOD_DEPS "Os_Stubs") + +set(UT_MOD_DEPS + Os_File_Posix +) +choose_fprime_implementation(Os/File Os_File_None) register_fprime_ut() diff --git a/Svc/CmdSequencer/FPrimeSequence.cpp b/Svc/CmdSequencer/FPrimeSequence.cpp index 637673726f..631bd8f1e5 100644 --- a/Svc/CmdSequencer/FPrimeSequence.cpp +++ b/Svc/CmdSequencer/FPrimeSequence.cpp @@ -163,7 +163,7 @@ namespace Svc { Fw::SerializeBufferBase& buffer = this->m_buffer; bool status = true; - NATIVE_INT_TYPE readLen = Sequence::Header::SERIALIZED_SIZE; + FwSignedSizeType readLen = Sequence::Header::SERIALIZED_SIZE; FW_ASSERT(readLen >= 0, readLen); const NATIVE_UINT_TYPE capacity = buffer.getBuffCapacity(); @@ -180,7 +180,7 @@ namespace Svc { if (fileStatus != Os::File::OP_OK) { this->m_events.fileInvalid( CmdSequencer_FileReadStage::READ_HEADER, - file.getLastError() + fileStatus ); status = false; } @@ -263,7 +263,7 @@ namespace Svc { const NATIVE_UINT_TYPE size = this->m_header.m_fileSize; Fw::SerializeBufferBase& buffer = this->m_buffer; - NATIVE_INT_TYPE readLen = size; + FwSignedSizeType readLen = size; Os::File::Status fileStatus = file.read( buffer.getBuffAddr(), readLen @@ -272,7 +272,7 @@ namespace Svc { if (fileStatus != Os::File::OP_OK) { this->m_events.fileInvalid( CmdSequencer_FileReadStage::READ_SEQ_DATA, - file.getLastError() + fileStatus ); return false; } diff --git a/Svc/CmdSequencer/formats/AMPCSSequence.cpp b/Svc/CmdSequencer/formats/AMPCSSequence.cpp index 5b39715501..d9957342e5 100644 --- a/Svc/CmdSequencer/formats/AMPCSSequence.cpp +++ b/Svc/CmdSequencer/formats/AMPCSSequence.cpp @@ -80,23 +80,19 @@ namespace Svc { getFileSize(const Fw::CmdStringArg& seqFileName) { bool status = true; - FwSizeType fileSize; + FwSignedSizeType fileSize; this->setFileName(seqFileName); const Os::FileSystem::Status fileStatus = Os::FileSystem::getFileSize(this->m_fileName.toChar(), fileSize); - // fileSize will be used to set a U32 member below, thus we check overflow first - bool overflow = static_cast(static_cast(fileSize)) != fileSize; if ( fileStatus == Os::FileSystem::OP_OK and - fileSize >= sizeof(this->m_sequenceHeader) and - !overflow + fileSize >= static_cast(sizeof(this->m_sequenceHeader)) ) { this->m_header.m_fileSize = static_cast(fileSize - sizeof(this->m_sequenceHeader)); } else { this->m_events.fileInvalid( - CmdSequencer_FileReadStage::READ_HEADER_SIZE, - overflow ? Os::FileSystem::OTHER_ERROR : fileStatus + CmdSequencer_FileReadStage::READ_HEADER_SIZE, fileStatus ); status = false; } @@ -205,7 +201,7 @@ namespace Svc { bool status = true; Fw::SerializeStatus ser_status; - NATIVE_INT_TYPE readLen = sizeof(U32); + FwSignedSizeType readLen = sizeof(U32); FW_ASSERT(readLen >= 0, readLen); ser_status = buffer.setBuffLen(readLen); @@ -217,7 +213,7 @@ namespace Svc { if (fileStatus != Os::File::OP_OK) { this->m_events.fileInvalid( CmdSequencer_FileReadStage::READ_SEQ_CRC, - file.getLastError() + fileStatus ); status = false; } @@ -277,7 +273,7 @@ namespace Svc { bool status = true; - NATIVE_INT_TYPE readLen = sizeof this->m_sequenceHeader; + FwSignedSizeType readLen = sizeof this->m_sequenceHeader; const Os::File::Status fileStatus = file.read( this->m_sequenceHeader, readLen @@ -286,7 +282,7 @@ namespace Svc { if (fileStatus != Os::File::OP_OK) { this->m_events.fileInvalid( CmdSequencer_FileReadStage::READ_HEADER, - file.getLastError() + fileStatus ); status = false; } @@ -318,13 +314,13 @@ namespace Svc { return false; } - NATIVE_INT_TYPE readLen = size; + FwSignedSizeType readLen = size; const Os::File::Status fileStatus = file.read(addr, readLen); // Check read status if (fileStatus != Os::File::OP_OK) { this->m_events.fileInvalid( CmdSequencer_FileReadStage::READ_SEQ_DATA, - file.getLastError() + fileStatus ); return false; } diff --git a/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp b/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp index a7d127f361..e6ef0a5492 100644 --- a/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp +++ b/Svc/CmdSequencer/test/ut/CmdSequencerTester.cpp @@ -9,7 +9,6 @@ // acknowledged. #include "Fw/Com/ComPacket.hpp" -#include "Os/Stubs/FileStubs.hpp" #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/SequenceFiles/FPrime/FPrime.hpp" #include "CmdSequencerTester.hpp" @@ -212,10 +211,10 @@ namespace Svc { file.getErrorInfo(errorInfo); const char *const errorFileName = errorInfo.open.fileName.toChar(); // Enable open interceptor - this->openInterceptor.enable(); + this->interceptor.enable(Interceptor::EnableType::OPEN); // DOESNT_EXIST { - this->openInterceptor.fileStatus = Os::File::DOESNT_EXIST; + this->interceptor.fileStatus = Os::File::Status::DOESNT_EXIST; // Validate the file this->sendCmd_CS_VALIDATE(0, 0, fileName); this->clearAndDispatch(); @@ -226,7 +225,7 @@ namespace Svc { } // NO_PERMISSION { - this->openInterceptor.fileStatus = Os::File::NO_PERMISSION; + this->interceptor.fileStatus = Os::File::NO_PERMISSION; // Validate the file const U32 validateCmdSeq = 14; this->sendCmd_CS_VALIDATE(0, validateCmdSeq, fileName); @@ -245,7 +244,7 @@ namespace Svc { ASSERT_EVENTS_CS_FileReadError(0, errorFileName); } // Disable open interceptor - this->openInterceptor.disable(); + this->interceptor.disable(); } void CmdSequencerTester :: @@ -262,14 +261,14 @@ namespace Svc { file.getErrorInfo(errorInfo); const char *const errorFileName = errorInfo.headerRead.fileName.toChar(); // Enable read interceptor - this->readInterceptor.enable(); + this->interceptor.enable(Interceptor::EnableType::READ); // Read error reading header { // Set up fault injection state - this->readInterceptor.waitCount = errorInfo.headerRead.waitCount; - this->readInterceptor.fileStatus = Os::File::NO_SPACE; - this->readInterceptor.errorType = Interceptors::Read::ErrorType::READ; - Os::setLastError(Os::File::NO_SPACE); + this->interceptor.waitCount = errorInfo.headerRead.waitCount; + this->interceptor.fileStatus = Os::File::NO_SPACE; + this->interceptor.errorType = Interceptor::ErrorType::READ; + //TODO: fix me Os::setLastError(Os::File::NO_SPACE); // Validate file const U32 validateCmdSeq = 14; this->sendCmd_CS_VALIDATE(0, validateCmdSeq, fileName); @@ -292,7 +291,7 @@ namespace Svc { ); } // Disable read interceptor - this->readInterceptor.disable(); + this->interceptor.disable(); } void CmdSequencerTester :: @@ -309,14 +308,14 @@ namespace Svc { file.getErrorInfo(errorInfo); const char *const errorFileName = errorInfo.dataRead.fileName.toChar(); // Enable read interceptor - this->readInterceptor.enable(); + this->interceptor.enable(Interceptor::EnableType::READ); // Read error reading data { // Set up fault injection state - this->readInterceptor.waitCount = errorInfo.dataRead.waitCount; - this->readInterceptor.fileStatus = Os::File::NO_SPACE; - this->readInterceptor.errorType = Interceptors::Read::ErrorType::READ; - Os::setLastError(Os::File::NO_SPACE); + this->interceptor.waitCount = errorInfo.dataRead.waitCount; + this->interceptor.fileStatus = Os::File::NO_SPACE; + this->interceptor.errorType = Interceptor::ErrorType::READ; + // Validate file const U32 validateCmdSeq = 14; this->sendCmd_CS_VALIDATE(0, validateCmdSeq, fileName); @@ -342,10 +341,10 @@ namespace Svc { // Size error reading data { // Set up fault injection state - this->readInterceptor.waitCount = errorInfo.dataRead.waitCount; - this->readInterceptor.fileStatus = Os::File::OP_OK; - this->readInterceptor.errorType = Interceptors::Read::ErrorType::SIZE; - this->readInterceptor.size = 2; + this->interceptor.waitCount = errorInfo.dataRead.waitCount; + this->interceptor.fileStatus = Os::File::OP_OK; + this->interceptor.errorType = Interceptor::ErrorType::SIZE; + this->interceptor.size = 2; // Validate file const U32 validateCmdSeq = 14; this->sendCmd_CS_VALIDATE(0, validateCmdSeq, fileName); @@ -369,7 +368,7 @@ namespace Svc { ); } // Disable read interceptor - this->readInterceptor.disable(); + this->interceptor.disable(); } void CmdSequencerTester :: @@ -817,5 +816,29 @@ namespace Svc { Fw::CmdResponse(Fw::CmdResponse::OK) ); } - +} // namespace Svc + +namespace Os { +//! Overrides the default delegate function with this one as it is defined in the local compilation archive +//! \param aligned_placement_new_memory: memory to fill +//! \param to_copy: possible copy +//! \return: new interceptor +FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) { + FW_ASSERT(aligned_placement_new_memory != nullptr); + const Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor* copy_me = + reinterpret_cast(to_copy); + // Placement-new the file handle into the opaque file-handle storage + static_assert(sizeof(Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor) <= sizeof Os::File::m_handle_storage, + "Handle size not large enough"); + static_assert((FW_HANDLE_ALIGNMENT % alignof(Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor)) == 0, + "Handle alignment invalid"); + Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor *interface = nullptr; + if (to_copy == nullptr) { + interface = new(aligned_placement_new_memory) Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor; + } else { + interface = new(aligned_placement_new_memory) Svc::CmdSequencerTester::Interceptor::PosixFileInterceptor(*copy_me); + } + FW_ASSERT(interface != nullptr); + return interface; +} } diff --git a/Svc/CmdSequencer/test/ut/CmdSequencerTester.hpp b/Svc/CmdSequencer/test/ut/CmdSequencerTester.hpp index 13135bca8b..827c593dbb 100644 --- a/Svc/CmdSequencer/test/ut/CmdSequencerTester.hpp +++ b/Svc/CmdSequencer/test/ut/CmdSequencerTester.hpp @@ -14,7 +14,7 @@ #include "Fw/Types/MallocAllocator.hpp" #include "CmdSequencerGTestBase.hpp" -#include "Os/File.hpp" +#include "Os/Posix/File.hpp" #include "Svc/CmdSequencer/CmdSequencerImpl.hpp" #include "Svc/CmdSequencer/formats/AMPCSSequence.hpp" #include "Svc/CmdSequencer/test/ut/SequenceFiles/SequenceFiles.hpp" @@ -75,71 +75,8 @@ namespace Svc { AMPCSSequence ampcsSequence; }; - - struct Interceptors { - - //! Open interceptor - class Open { - - public: - - // ---------------------------------------------------------------------- - // Constructors - // ---------------------------------------------------------------------- - - Open(); - - public: - - // ---------------------------------------------------------------------- - // Public instance methods - // ---------------------------------------------------------------------- - - //! Enable the interceptor - void enable(); - - //! Disable the interceptor - void disable(); - - private: - - // ---------------------------------------------------------------------- - // Private instance methods - // ---------------------------------------------------------------------- - - //! Intercept an open request - //! \return Success or failure - bool intercept( - Os::File::Status &fileStatus //!< The returned file status - ); - - private: - - // ---------------------------------------------------------------------- - // Private static methods - // ---------------------------------------------------------------------- - - //! Register function - static bool registerFunction( - Os::File::Status& fileStatus, - const char* fileName, - Os::File::Mode mode, - void* ptr - ); - - public: - - // ---------------------------------------------------------------------- - // Public member variables - // ---------------------------------------------------------------------- - - //! File status - Os::File::Status fileStatus; - - }; - - //! Read interceptor - class Read { + public: + class Interceptor { public: @@ -148,24 +85,34 @@ namespace Svc { // ---------------------------------------------------------------------- //! Type of injected errors - struct ErrorType { + struct EnableType { typedef enum { - NONE, // Don't inject any errors - READ, // Bad read status - SIZE, // Bad size - DATA // Unexpected data + NONE, // Don't enable interception + OPEN, // Intercept opens + READ, // Intercept reads } t; - }; + //! Type of injected errors + struct ErrorType { + + typedef enum { + NONE, // Don't inject any errors + READ, // Bad read status + SIZE, // Bad size + DATA // Unexpected data + } t; + + }; + public: // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- - Read(); + Interceptor(); public: @@ -174,45 +121,31 @@ namespace Svc { // ---------------------------------------------------------------------- //! Enable the interceptor - void enable(); + void enable(EnableType::t enableType); //! Disable the interceptor void disable(); - private: - - // ---------------------------------------------------------------------- - // Private instance methods - // ---------------------------------------------------------------------- - - //! Intercept an open request - //! \return Success or failure - bool intercept( - Os::File::Status &fileStatus, - void *buffer, - NATIVE_INT_TYPE &size - ); + class PosixFileInterceptor : public Os::Posix::File::PosixFile { + friend class Interceptor; + public: + PosixFileInterceptor() = default; - private: + PosixFileInterceptor(const PosixFileInterceptor& other) = default; - // ---------------------------------------------------------------------- - // Private static methods - // ---------------------------------------------------------------------- + Os::FileInterface::Status open(const char *path, Mode mode, OverwriteType overwrite) override; - //! Register function - static bool registerFunction( - Os::File::Status &stat, - void *buffer, - NATIVE_INT_TYPE &size, - bool waitForFull, - void *ptr - ); + Status read(U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + //! Current interceptor + static Interceptor* s_current_interceptor; + }; public: // ---------------------------------------------------------------------- // Public member variables // ---------------------------------------------------------------------- + EnableType::t enabled; //! Error type ErrorType::t errorType; @@ -231,9 +164,7 @@ namespace Svc { }; - }; - public: // ---------------------------------------------------------------------- // Construction and destruction @@ -479,12 +410,9 @@ namespace Svc { //! The allocator Fw::MallocAllocator mallocator; - //! Open interceptor - Interceptors::Open openInterceptor; - - //! Read interceptor - Interceptors::Read readInterceptor; + //! Open/Read interceptor + Interceptor interceptor; }; } diff --git a/Svc/CmdSequencer/test/ut/Immediate.cpp b/Svc/CmdSequencer/test/ut/Immediate.cpp index 4a6e315fe3..e388869902 100644 --- a/Svc/CmdSequencer/test/ut/Immediate.cpp +++ b/Svc/CmdSequencer/test/ut/Immediate.cpp @@ -11,7 +11,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/Immediate.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/ImmediateBase.cpp b/Svc/CmdSequencer/test/ut/ImmediateBase.cpp index 988cc7c955..110f4313e2 100644 --- a/Svc/CmdSequencer/test/ut/ImmediateBase.cpp +++ b/Svc/CmdSequencer/test/ut/ImmediateBase.cpp @@ -12,7 +12,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/ImmediateBase.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/ImmediateEOS.cpp b/Svc/CmdSequencer/test/ut/ImmediateEOS.cpp index bf562d7639..274cb1c2ed 100644 --- a/Svc/CmdSequencer/test/ut/ImmediateEOS.cpp +++ b/Svc/CmdSequencer/test/ut/ImmediateEOS.cpp @@ -11,7 +11,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/ImmediateEOS.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/Interceptors.cpp b/Svc/CmdSequencer/test/ut/Interceptors.cpp index c303c59026..1edd489297 100644 --- a/Svc/CmdSequencer/test/ut/Interceptors.cpp +++ b/Svc/CmdSequencer/test/ut/Interceptors.cpp @@ -9,125 +9,70 @@ // acknowledged. // ====================================================================== -#include "Os/Stubs/FileStubs.hpp" +#include "Os/Stub/test/File.hpp" #include "CmdSequencerTester.hpp" #include "gtest/gtest.h" namespace Svc { - CmdSequencerTester::Interceptors::Open :: - Open() : - fileStatus(Os::File::OP_OK) - { - - } - - void CmdSequencerTester::Interceptors::Open :: - enable() - { - Os::registerOpenInterceptor(registerFunction, this); - } - - void CmdSequencerTester::Interceptors::Open :: - disable() - { - Os::clearOpenInterceptor(); - } - - bool CmdSequencerTester::Interceptors::Open :: - intercept(Os::File::Status& fileStatus) - { - fileStatus = this->fileStatus; - return fileStatus == Os::File::OP_OK; - } - - bool CmdSequencerTester::Interceptors::Open :: - registerFunction( - Os::File::Status& fileStatus, - const char* fileName, - Os::File::Mode mode, - void* ptr - ) - { - EXPECT_TRUE(ptr); - Open *const open = static_cast(ptr); - return open->intercept(fileStatus); - } - - CmdSequencerTester::Interceptors::Read :: - Read() : - errorType(ErrorType::NONE), - waitCount(0), - size(0), - fileStatus(Os::File::OP_OK) - { - - } - - void CmdSequencerTester::Interceptors::Read :: - enable() - { - Os::registerReadInterceptor(registerFunction, this); - } - - void CmdSequencerTester::Interceptors::Read :: - disable() - { - Os::clearReadInterceptor(); - } - - bool CmdSequencerTester::Interceptors::Read :: - intercept( - Os::File::Status& fileStatus, - void *buffer, - NATIVE_INT_TYPE &size - ) - { - bool status; - if (this->errorType == ErrorType::NONE) { - // No errors: return true - status = true; + CmdSequencerTester::Interceptor* CmdSequencerTester::Interceptor::PosixFileInterceptor::s_current_interceptor = nullptr; + CmdSequencerTester::Interceptor::Interceptor() : + enabled(EnableType::NONE), + errorType(ErrorType::NONE), + waitCount(0), + size(0), + fileStatus(Os::File::OP_OK) { + CmdSequencerTester::Interceptor::PosixFileInterceptor::s_current_interceptor = this; } - else if (this->waitCount > 0) { - // Not time to inject an error yet: decrement wait count - --this->waitCount; - status = true; + + void CmdSequencerTester::Interceptor::enable(EnableType::t enableType) { + this->enabled = enableType; } - else { - // Time to inject an error: check test scenario - switch (this->errorType) { - case ErrorType::READ: - fileStatus = this->fileStatus; - break; - case ErrorType::SIZE: - size = this->size; - fileStatus = Os::File::OP_OK; - break; - case Read::ErrorType::DATA: - memcpy(buffer, this->data, size); - fileStatus = Os::File::OP_OK; - break; - default: - EXPECT_TRUE(false); - break; - } - status = false; + + void CmdSequencerTester::Interceptor::disable() { + this->enabled = EnableType::t::NONE; } - return status; - } - bool CmdSequencerTester::Interceptors::Read :: - registerFunction( - Os::File::Status& fileStatus, - void * buffer, - NATIVE_INT_TYPE &size, - bool waitForFull, - void* ptr - ) - { - EXPECT_TRUE(ptr); - Read *const read = static_cast(ptr); - return read->intercept(fileStatus, buffer, size); - } + Os::FileInterface::Status CmdSequencerTester::Interceptor::PosixFileInterceptor::open(const char *path, Mode mode, OverwriteType overwrite) { + if ((s_current_interceptor != nullptr) && (s_current_interceptor ->enabled == EnableType::t::OPEN)) { + return s_current_interceptor->fileStatus; + } + return this->Os::Posix::File::PosixFile::open(path, mode, overwrite); + } + Os::File::Status CmdSequencerTester::Interceptor::PosixFileInterceptor::read( + U8 *buffer, + FwSignedSizeType &requestSize, + Os::File::WaitType waitType + ) { + (void) waitType; + Os::File::Status status = this->Os::Posix::File::PosixFile::read(buffer, requestSize, waitType); + if (s_current_interceptor == nullptr) { + return status; + } else if ((s_current_interceptor->enabled == EnableType::READ) && (s_current_interceptor->errorType != ErrorType::NONE)) { + if (s_current_interceptor->waitCount > 0) { + // Not time to inject an error yet: decrement wait count + --s_current_interceptor->waitCount; + } else { + // Time to inject an error: check test scenario + switch (s_current_interceptor->errorType) { + case ErrorType::READ: + status = s_current_interceptor->fileStatus; + break; + case ErrorType::SIZE: + requestSize = s_current_interceptor->size; + status = Os::File::OP_OK; + break; + case ErrorType::DATA: + memcpy(buffer, s_current_interceptor->data, s_current_interceptor->size); + status = Os::File::OP_OK; + break; + default: + EXPECT_TRUE(false); + break; + } + } + } + return status; + } } diff --git a/Svc/CmdSequencer/test/ut/Mixed.cpp b/Svc/CmdSequencer/test/ut/Mixed.cpp index f69cfce0ae..6ef8517017 100644 --- a/Svc/CmdSequencer/test/ut/Mixed.cpp +++ b/Svc/CmdSequencer/test/ut/Mixed.cpp @@ -11,7 +11,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/Mixed.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/MixedRelativeBase.cpp b/Svc/CmdSequencer/test/ut/MixedRelativeBase.cpp index 2925563f1e..5093a14b3d 100644 --- a/Svc/CmdSequencer/test/ut/MixedRelativeBase.cpp +++ b/Svc/CmdSequencer/test/ut/MixedRelativeBase.cpp @@ -10,7 +10,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/MixedRelativeBase.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/NoFiles.cpp b/Svc/CmdSequencer/test/ut/NoFiles.cpp index b2c94bf049..65657e05aa 100644 --- a/Svc/CmdSequencer/test/ut/NoFiles.cpp +++ b/Svc/CmdSequencer/test/ut/NoFiles.cpp @@ -11,7 +11,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/NoFiles.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/Readme.txt b/Svc/CmdSequencer/test/ut/Readme.txt deleted file mode 100644 index a4fff6d17d..0000000000 --- a/Svc/CmdSequencer/test/ut/Readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -This test can be run by executing the following: - -From Svc/CmdSequencer: - -"make ut run_ut" - -The test will return a pass/fail error code depending on the -success of the test. \ No newline at end of file diff --git a/Svc/CmdSequencer/test/ut/Relative.cpp b/Svc/CmdSequencer/test/ut/Relative.cpp index fd7fa7d6c5..834eecd878 100644 --- a/Svc/CmdSequencer/test/ut/Relative.cpp +++ b/Svc/CmdSequencer/test/ut/Relative.cpp @@ -11,7 +11,6 @@ #include "Svc/CmdSequencer/test/ut/CommandBuffers.hpp" #include "Svc/CmdSequencer/test/ut/Relative.hpp" -#include "Os/Stubs/FileStubs.hpp" namespace Svc { diff --git a/Svc/CmdSequencer/test/ut/SequenceFiles/AMPCS/CRCs.cpp b/Svc/CmdSequencer/test/ut/SequenceFiles/AMPCS/CRCs.cpp index 6384897a32..f5862ad496 100644 --- a/Svc/CmdSequencer/test/ut/SequenceFiles/AMPCS/CRCs.cpp +++ b/Svc/CmdSequencer/test/ut/SequenceFiles/AMPCS/CRCs.cpp @@ -43,14 +43,14 @@ namespace Svc { //! Write a file void writeFile( Os::File& file, //!< The file - const void *const buffer, //!< The buffer + const U8 *buffer, //!< The buffer const U32 size //!< The number of bytes to write ) { - NATIVE_INT_TYPE sizeThenActualSize = size; + FwSignedSizeType sizeThenActualSize = size; const Os::File::Status status = file.write( buffer, sizeThenActualSize, - false + Os::File::WaitType::WAIT ); ASSERT_EQ(Os::File::OP_OK, status); const U32 actualSize = sizeThenActualSize; diff --git a/Svc/CmdSequencer/test/ut/SequenceFiles/Buffers.cpp b/Svc/CmdSequencer/test/ut/SequenceFiles/Buffers.cpp index d6bcc10bdf..d077805881 100644 --- a/Svc/CmdSequencer/test/ut/SequenceFiles/Buffers.cpp +++ b/Svc/CmdSequencer/test/ut/SequenceFiles/Buffers.cpp @@ -43,11 +43,11 @@ namespace Svc { ) { Os::File file; ASSERT_EQ(file.open(fileName, Os::File::OPEN_WRITE), Os::File::OP_OK); - NATIVE_INT_TYPE size = buffer.getBuffLength(); + FwSignedSizeType size = buffer.getBuffLength(); const U32 expectedSize = size; const U8 *const buffAddr = buffer.getBuffAddr(); ASSERT_EQ( - file.write(buffAddr, size, true), + file.write(buffAddr, size, Os::File::WaitType::WAIT), Os::File::OP_OK ); ASSERT_EQ(expectedSize, static_cast(size)); diff --git a/Svc/ComLogger/ComLogger.cpp b/Svc/ComLogger/ComLogger.cpp index b03617539d..533d7311dd 100644 --- a/Svc/ComLogger/ComLogger.cpp +++ b/Svc/ComLogger/ComLogger.cpp @@ -262,8 +262,8 @@ namespace Svc { U16 length ) { - NATIVE_INT_TYPE size = length; - Os::File::Status ret = m_file.write(data, size); + FwSignedSizeType size = length; + Os::File::Status ret = m_file.write(reinterpret_cast(data), size); if( Os::File::OP_OK != ret || size != static_cast(length) ) { if( !this->m_writeErrorOccurred ) { // throttle this event, otherwise a positive // feedback event loop can occur! diff --git a/Svc/ComLogger/test/ut/ComLoggerTester.cpp b/Svc/ComLogger/test/ut/ComLoggerTester.cpp index 53acddc0e2..6618b16624 100644 --- a/Svc/ComLogger/test/ut/ComLoggerTester.cpp +++ b/Svc/ComLogger/test/ut/ComLoggerTester.cpp @@ -87,7 +87,7 @@ namespace Svc { CHAR hashFileName[2048]; CHAR prevHashFileName[2048]; U8 buf[1024]; - NATIVE_INT_TYPE length; + FwSignedSizeType length; U16 bufferSize = 0; Os::File::Status ret; Os::File file; @@ -150,7 +150,7 @@ namespace Svc { // Make sure the file size is smaller or equal to the limit: Os::FileSystem::Status fsStat; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; fsStat = Os::FileSystem::getFileSize(fileName, fileSize); //!< gets the size of the file (in bytes) at location path ASSERT_EQ(fsStat, Os::FileSystem::OP_OK); ASSERT_LE(fileSize, MAX_BYTES_PER_FILE); @@ -163,8 +163,8 @@ namespace Svc { for(int i = 0; i < 5; i++) { // Get length of buffer to read - NATIVE_INT_TYPE length = sizeof(U16); - ret = file.read(&buf, length); + FwSignedSizeType length = sizeof(U16); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK, ret); ASSERT_EQ(length, static_cast(sizeof(U16))); Fw::SerialBuffer comBuffLength(buf, length); @@ -175,7 +175,7 @@ namespace Svc { // Read and check buffer: length = bufferSize; - ret = file.read(&buf, length); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, static_cast(bufferSize)); ASSERT_EQ(memcmp(buf, data, COM_BUFFER_LENGTH), 0); @@ -187,7 +187,7 @@ namespace Svc { // Make sure we reached the end of the file: length = sizeof(NATIVE_INT_TYPE); - ret = file.read(&buf, length); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, 0); file.close(); @@ -209,7 +209,7 @@ namespace Svc { CHAR hashFileName[2048]; CHAR prevHashFileName[2048]; U8 buf[1024]; - NATIVE_INT_TYPE length; + FwSignedSizeType length; Os::File::Status ret; Os::File file; @@ -273,7 +273,7 @@ namespace Svc { // Make sure the file size is smaller or equal to the limit: Os::FileSystem::Status fsStat; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; fsStat = Os::FileSystem::getFileSize(fileName, fileSize); //!< gets the size of the file (in bytes) at location path ASSERT_EQ(fsStat, Os::FileSystem::OP_OK); ASSERT_LE(fileSize, MAX_BYTES_PER_FILE); @@ -286,8 +286,8 @@ namespace Svc { for(int i = 0; i < 5; i++) { // Get length of buffer to read - NATIVE_INT_TYPE length = COM_BUFFER_LENGTH; - ret = file.read(&buf, length); + FwSignedSizeType length = COM_BUFFER_LENGTH; + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, COM_BUFFER_LENGTH); ASSERT_EQ(memcmp(buf, data, COM_BUFFER_LENGTH), 0); @@ -299,7 +299,7 @@ namespace Svc { // Make sure we reached the end of the file: length = sizeof(NATIVE_INT_TYPE); - ret = file.read(&buf, length); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, 0); file.close(); @@ -575,7 +575,7 @@ namespace Svc { CHAR hashFileName[2048]; CHAR prevHashFileName[2048]; U8 buf[1024]; - NATIVE_INT_TYPE length; + FwSignedSizeType length; U16 bufferSize = 0; Os::File::Status ret; Os::File file; @@ -640,7 +640,7 @@ namespace Svc { // Make sure the file size is smaller or equal to the limit: Os::FileSystem::Status fsStat; - FwSizeType fileSize = 0; + FwSignedSizeType fileSize = 0; fsStat = Os::FileSystem::getFileSize(fileName, fileSize); //!< gets the size of the file (in bytes) at location path ASSERT_EQ(fsStat, Os::FileSystem::OP_OK); ASSERT_LE(fileSize, MAX_BYTES_PER_FILE); @@ -653,8 +653,8 @@ namespace Svc { for(int i = 0; i < 5; i++) { // Get length of buffer to read - NATIVE_INT_TYPE length = sizeof(U16); - ret = file.read(&buf, length); + FwSignedSizeType length = sizeof(U16); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK, ret); ASSERT_EQ(length, static_cast(sizeof(U16))); Fw::SerialBuffer comBuffLength(buf, length); @@ -665,7 +665,7 @@ namespace Svc { // Read and check buffer: length = bufferSize; - ret = file.read(&buf, length); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, static_cast(bufferSize)); ASSERT_EQ(memcmp(buf, data, COM_BUFFER_LENGTH), 0); @@ -673,7 +673,7 @@ namespace Svc { // Make sure we reached the end of the file: length = sizeof(NATIVE_INT_TYPE); - ret = file.read(&buf, length); + ret = file.read(buf, length); ASSERT_EQ(Os::File::OP_OK,ret); ASSERT_EQ(length, 0); file.close(); diff --git a/Svc/FileDownlink/File.cpp b/Svc/FileDownlink/File.cpp index 830a390dd6..6e22bb21c7 100644 --- a/Svc/FileDownlink/File.cpp +++ b/Svc/FileDownlink/File.cpp @@ -33,13 +33,13 @@ namespace Svc { this->m_destName = destLogStringArg; // Set size - FwSizeType file_size; - const Os::FileSystem::Status status = + FwSignedSizeType file_size; + const Os::FileSystem::Status status = Os::FileSystem::getFileSize(sourceFileName, file_size); if (status != Os::FileSystem::OP_OK) return Os::File::BAD_SIZE; // If the size does not cast cleanly to the desired U32 type, return size error - if (static_cast(static_cast(file_size)) != file_size) { + if (static_cast(static_cast(file_size)) != file_size) { return Os::File::BAD_SIZE; } this->m_size = static_cast(file_size); @@ -62,14 +62,17 @@ namespace Svc { { Os::File::Status status; - status = this->m_osFile.seek(byteOffset); - if (status != Os::File::OP_OK) - return status; + status = this->m_osFile.seek(byteOffset, Os::File::SeekType::ABSOLUTE); + if (status != Os::File::OP_OK) { + return status; + } - NATIVE_INT_TYPE intSize = size; + FwSignedSizeType intSize = size; status = this->m_osFile.read(data, intSize); - if (status != Os::File::OP_OK) - return status; + + if (status != Os::File::OP_OK) { + return status; + } // Force a bad size error when the U32 carrying size is bad if (static_cast(intSize) != size) { return Os::File::BAD_SIZE; diff --git a/Svc/FileDownlink/test/ut/FileBuffer.cpp b/Svc/FileDownlink/test/ut/FileBuffer.cpp index f3a8abb573..520ec7b8b3 100644 --- a/Svc/FileDownlink/test/ut/FileBuffer.cpp +++ b/Svc/FileDownlink/test/ut/FileBuffer.cpp @@ -61,7 +61,7 @@ namespace Svc { FW_ASSERT(status == Os::File::OP_OK); const U32 size = this->index; - NATIVE_INT_TYPE intSize = size; + FwSignedSizeType intSize = size; status = file.write(this->data, intSize); FW_ASSERT(status == Os::File::OP_OK); FW_ASSERT(static_cast(intSize) == size); diff --git a/Svc/FileManager/FileManager.cpp b/Svc/FileManager/FileManager.cpp index eabc57480f..09823c19d4 100644 --- a/Svc/FileManager/FileManager.cpp +++ b/Svc/FileManager/FileManager.cpp @@ -234,7 +234,7 @@ namespace Svc { Fw::LogStringArg logStringFileName(fileName.toChar()); this->log_ACTIVITY_HI_FileSizeStarted(logStringFileName); - FwSizeType size_arg; + FwSignedSizeType size_arg; const Os::FileSystem::Status status = Os::FileSystem::getFileSize(fileName.toChar(), size_arg); if (status != Os::FileSystem::OP_OK) { diff --git a/Svc/FileUplink/File.cpp b/Svc/FileUplink/File.cpp index 55a99682e3..3743295242 100644 --- a/Svc/FileUplink/File.cpp +++ b/Svc/FileUplink/File.cpp @@ -40,14 +40,14 @@ namespace Svc { { Os::File::Status status; - status = this->osFile.seek(byteOffset); + status = this->osFile.seek(byteOffset, Os::File::SeekType::ABSOLUTE); if (status != Os::File::OP_OK) { return status; } - NATIVE_INT_TYPE intLength = length; + FwSignedSizeType intLength = length; //Note: not waiting for the file write to finish - status = this->osFile.write(data, intLength, false); + status = this->osFile.write(data, intLength, Os::File::WaitType::NO_WAIT); if (status != Os::File::OP_OK) { return status; } diff --git a/Svc/FileUplink/test/ut/FileUplinkTester.cpp b/Svc/FileUplink/test/ut/FileUplinkTester.cpp index 0e923da8a8..669de20095 100644 --- a/Svc/FileUplink/test/ut/FileUplinkTester.cpp +++ b/Svc/FileUplink/test/ut/FileUplinkTester.cpp @@ -641,7 +641,7 @@ namespace Svc { file.open(path, Os::File::OPEN_READ); - NATIVE_INT_TYPE intSize = static_cast(dataSize); + FwSignedSizeType intSize = static_cast(dataSize); const Os::File::Status status = file.read(fileData, intSize); ASSERT_EQ(Os::File::OP_OK, status); diff --git a/Svc/PrmDb/CMakeLists.txt b/Svc/PrmDb/CMakeLists.txt index 2aa37c969c..78714c2f94 100644 --- a/Svc/PrmDb/CMakeLists.txt +++ b/Svc/PrmDb/CMakeLists.txt @@ -14,12 +14,15 @@ set(SOURCE_FILES register_fprime_module() ### UTs ### -set(UT_MOD_DEPS - Os_Stubs -) set(UT_SOURCE_FILES "${FPRIME_FRAMEWORK_PATH}/Svc/PrmDb/PrmDb.fpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/PrmDbTester.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/PrmDbImplTester.cpp" ) + +set(UT_MOD_DEPS + Os_File_Test_Stub +) +choose_fprime_implementation(Os/File Os/File/None) register_fprime_ut() + diff --git a/Svc/PrmDb/PrmDbImpl.cpp b/Svc/PrmDb/PrmDbImpl.cpp index a4cceb7e06..c75219266a 100644 --- a/Svc/PrmDb/PrmDbImpl.cpp +++ b/Svc/PrmDb/PrmDbImpl.cpp @@ -150,8 +150,8 @@ namespace Svc { if (this->m_db[entry].used) { // write delimiter static const U8 delim = PRMDB_ENTRY_DELIMITER; - NATIVE_INT_TYPE writeSize = sizeof(delim); - stat = paramFile.write(&delim,writeSize,true); + FwSignedSizeType writeSize = static_cast(sizeof(delim)); + stat = paramFile.write(&delim,writeSize,Os::File::WaitType::WAIT); if (stat != Os::File::OP_OK) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::DELIMITER,numRecords,stat); @@ -175,14 +175,14 @@ namespace Svc { // write record size writeSize = buff.getBuffLength(); - stat = paramFile.write(buff.getBuffAddr(),writeSize,true); + stat = paramFile.write(buff.getBuffAddr(),writeSize,Os::File::WaitType::WAIT); if (stat != Os::File::OP_OK) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::RECORD_SIZE,numRecords,stat); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); return; } - if (writeSize != sizeof(writeSize)) { + if (writeSize != sizeof(recordSize)) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::RECORD_SIZE_SIZE,numRecords,writeSize); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); @@ -200,14 +200,14 @@ namespace Svc { // write parameter ID writeSize = buff.getBuffLength(); - stat = paramFile.write(buff.getBuffAddr(),writeSize,true); + stat = paramFile.write(buff.getBuffAddr(),writeSize,Os::File::WaitType::WAIT); if (stat != Os::File::OP_OK) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_ID,numRecords,stat); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); return; } - if (writeSize != static_cast(buff.getBuffLength())) { + if (writeSize != static_cast(buff.getBuffLength())) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_ID_SIZE,numRecords,writeSize); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); @@ -217,14 +217,14 @@ namespace Svc { // write serialized parameter value writeSize = this->m_db[entry].val.getBuffLength(); - stat = paramFile.write(this->m_db[entry].val.getBuffAddr(),writeSize,true); + stat = paramFile.write(this->m_db[entry].val.getBuffAddr(),writeSize,Os::File::WaitType::WAIT); if (stat != Os::File::OP_OK) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_VALUE,numRecords,stat); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); return; } - if (writeSize != static_cast(this->m_db[entry].val.getBuffLength())) { + if (writeSize != static_cast(this->m_db[entry].val.getBuffLength())) { this->unLock(); this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::PARAMETER_VALUE_SIZE,numRecords,writeSize); this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); @@ -263,10 +263,10 @@ namespace Svc { for (NATIVE_INT_TYPE entry = 0; entry < PRMDB_NUM_DB_ENTRIES; entry++) { U8 delimiter; - NATIVE_INT_TYPE readSize = sizeof(delimiter); + FwSignedSizeType readSize = static_cast(sizeof(delimiter)); // read delimiter - Os::File::Status fStat = paramFile.read(&delimiter,readSize,true); + Os::File::Status fStat = paramFile.read(&delimiter,readSize,Os::File::WaitType::WAIT); // check for end of file (read size 0) if (0 == readSize) { @@ -292,7 +292,7 @@ namespace Svc { // read record size readSize = sizeof(recordSize); - fStat = paramFile.read(buff.getBuffAddr(),readSize,true); + fStat = paramFile.read(buff.getBuffAddr(),readSize,Os::File::WaitType::WAIT); if (fStat != Os::File::OP_OK) { this->log_WARNING_HI_PrmFileReadError(PrmReadError::RECORD_SIZE,recordNum,fStat); return; @@ -320,14 +320,14 @@ namespace Svc { // read the parameter ID FwPrmIdType parameterId = 0; - readSize = sizeof(FwPrmIdType); + readSize = static_cast(sizeof(FwPrmIdType)); - fStat = paramFile.read(buff.getBuffAddr(),readSize,true); + fStat = paramFile.read(buff.getBuffAddr(),readSize,Os::File::WaitType::WAIT); if (fStat != Os::File::OP_OK) { this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_ID,recordNum,fStat); return; } - if (sizeof(parameterId) != static_cast(readSize)) { + if (sizeof(parameterId) != static_cast(readSize)) { this->log_WARNING_HI_PrmFileReadError(PrmReadError::PARAMETER_ID_SIZE,recordNum,readSize); return; } diff --git a/Svc/PrmDb/test/ut/PrmDbImplTester.cpp b/Svc/PrmDb/test/ut/PrmDbImplTester.cpp index cb0d07d642..3fb56da69c 100644 --- a/Svc/PrmDb/test/ut/PrmDbImplTester.cpp +++ b/Svc/PrmDb/test/ut/PrmDbImplTester.cpp @@ -8,8 +8,7 @@ #include #include #include -#include - +#include #include #include @@ -21,7 +20,6 @@ namespace Svc { typedef PrmDb_PrmReadError PrmReadError; void PrmDbImplTester::runNominalPopulate() { - // clear database this->m_impl.clearDb(); @@ -115,6 +113,8 @@ namespace Svc { } void PrmDbImplTester::runNominalSaveFile() { + Os::Stub::File::Test::StaticData::setWriteResult(m_io_data, sizeof m_io_data); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); // fill with data this->runNominalPopulate(); // save the data @@ -139,6 +139,12 @@ namespace Svc { } void PrmDbImplTester::runNominalLoadFile() { + // Preconditions to populate the write file + this->runNominalSaveFile(); + + Os::Stub::File::Test::StaticData::setReadResult(m_io_data, Os::Stub::File::Test::StaticData::data.pointer); + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + // save the data this->clearEvents(); @@ -192,7 +198,6 @@ namespace Svc { this->clearEvents(); // write too many entries for (FwPrmIdType entry = 0; entry <= PRMDB_NUM_DB_ENTRIES; entry++) { - Fw::ParamBuffer pBuff; EXPECT_EQ(Fw::FW_SERIALIZE_OK,pBuff.serialize(static_cast(10))); this->invoke_to_setPrm(0,entry,pBuff); // dispatch message @@ -346,234 +351,166 @@ namespace Svc { PrmDbGTestBase::init(); } + PrmDbImplTester* PrmDbImplTester::PrmDbTestFile::s_tester = nullptr; -void PrmDbImplTester::runFileReadError() { - - // File open error - - this->clearEvents(); - // register interceptor - Os::registerOpenInterceptor(this->OpenInterceptor,static_cast(this)); - this->m_testOpenStatus = Os::File::DOESNT_EXIST; - // call function to read parameter file - this->m_impl.readParamFile(); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::OPEN,0,Os::File::DOESNT_EXIST); - Os::clearOpenInterceptor(); - - // Test delimiter read error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // delimiter is first read - this->m_readsToWait = 0; - // set read status to bad - this->m_testReadStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_readTestType = FILE_READ_READ_ERROR; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::DELIMITER,0,Os::File::NOT_OPENED); - Os::clearReadInterceptor(); - - // Test delimiter read size error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // delimiter is first read - this->m_readsToWait = 0; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_SIZE_ERROR; - // set size to size of byte + 1 - this->m_readSize = sizeof(U8)+1; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::DELIMITER_SIZE,0,sizeof(U8) + 1); - Os::clearReadInterceptor(); - - // Test delimiter value error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // delimiter is first read - this->m_readsToWait = 0; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_DATA_ERROR; - // set incorrect read value - this->m_readData[0] = 0x11; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::DELIMITER_VALUE,0,0x11); - - Os::clearReadInterceptor(); - - // Test record size read error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // size is second read - this->m_readsToWait = 1; - // set read status to bad - this->m_testReadStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_readTestType = FILE_READ_READ_ERROR; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::RECORD_SIZE,0,Os::File::NOT_OPENED); - - Os::clearReadInterceptor(); - - // Test record size read size error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // size is second read - this->m_readsToWait = 1; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_SIZE_ERROR; - // set size to size of byte + 1 - this->m_readSize = sizeof(U32)+1; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::RECORD_SIZE_SIZE,0,sizeof(U32)+1); - - Os::clearReadInterceptor(); - - // Test record size value too big error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // size is second read - this->m_readsToWait = 1; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_DATA_ERROR; - Fw::ParamBuffer pBuff; - pBuff.serialize(static_cast(FW_PARAM_BUFFER_MAX_SIZE + sizeof(U32) + 1)); - memcpy(this->m_readData,pBuff.getBuffAddr(),pBuff.getBuffLength()); - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::RECORD_SIZE_VALUE,0,FW_PARAM_BUFFER_MAX_SIZE + sizeof(U32) + 1); - - Os::clearReadInterceptor(); - - // Test parameter ID read error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // ID is third read - this->m_readsToWait = 2; - // set read status to bad - this->m_testReadStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_readTestType = FILE_READ_READ_ERROR; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::PARAMETER_ID,0,Os::File::NOT_OPENED); - - Os::clearReadInterceptor(); - - // Test parameter ID read size error - - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // size is second read - this->m_readsToWait = 2; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_SIZE_ERROR; - // set size to size of byte + 1 - this->m_readSize = sizeof(FwPrmIdType)+1; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::PARAMETER_ID_SIZE,0,sizeof(FwPrmIdType)+1); - - Os::clearReadInterceptor(); + void PrmDbImplTester::PrmDbTestFile::setTester(Svc::PrmDbImplTester *tester) { + ASSERT_NE(tester, nullptr); + s_tester = tester; + } - // Test parameter value read error + Os::File::Status PrmDbImplTester::PrmDbTestFile::read(U8 *buffer, FwSignedSizeType &size, Os::File::WaitType wait) { + EXPECT_NE(s_tester, nullptr); + Os::File::Status status = this->Os::Stub::File::Test::TestFile::read(buffer, size, wait); + if (s_tester->m_waits == 0) { + switch (s_tester->m_errorType) { + case FILE_STATUS_ERROR: + status = s_tester->m_status; + break; + case FILE_SIZE_ERROR: + size += 1; + break; + case FILE_DATA_ERROR: + buffer[0] += 1; + break; + default: + break; + } + } else { + s_tester->m_waits -= 1; + } + return status; + } - this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // record is fourth read - this->m_readsToWait = 3; - // set read status to bad - this->m_testReadStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_readTestType = FILE_READ_READ_ERROR; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::PARAMETER_VALUE,0,Os::File::NOT_OPENED); + Os::File::Status PrmDbImplTester::PrmDbTestFile::write(const U8* buffer, FwSignedSizeType &size, Os::File::WaitType wait) { + EXPECT_NE(s_tester, nullptr); + Os::File::Status status = this->Os::Stub::File::Test::TestFile::write(buffer, size, wait); + if (s_tester->m_waits == 0) { + switch (s_tester->m_errorType) { + case FILE_STATUS_ERROR: + status = s_tester->m_status; + break; + case FILE_SIZE_ERROR: + size += 1; + break; + default: + break; + } + } else { + s_tester->m_waits -= 1; + } + return status; + } - Os::clearReadInterceptor(); + void PrmDbImplTester::runFileReadError() { + // Preconditions setup and test + this->runNominalLoadFile(); - // Test parameter value read size error this->clearEvents(); - Os::registerReadInterceptor(this->ReadInterceptor,static_cast(this)); - // record is fourth read - this->m_readsToWait = 3; - // set read status to okay - this->m_testReadStatus = Os::File::OP_OK; - // set test type to read error - this->m_readTestType = FILE_READ_SIZE_ERROR; - // set size to size of U32 + 1, which is nominal populate test value - this->m_readSize = sizeof(U32)+1; - // call function to read file - this->m_impl.readParamFile(); - // check event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileReadError_SIZE(1); - ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::PARAMETER_VALUE_SIZE,0,sizeof(U32)+1); - - Os::clearReadInterceptor(); - + // Loop through all size errors testing each + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + this->m_errorType = FILE_SIZE_ERROR; + for (FwSizeType i = 0; i < 4; i++) { + clearEvents(); + this->m_waits = i; + this->m_impl.readParamFile(); + ASSERT_EVENTS_SIZE(1); + switch (i) { + case 0: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::DELIMITER_SIZE, 0, sizeof(U8) + 1); + break; + case 1: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE_SIZE, 0, sizeof(U32) + 1); + break; + case 2: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::PARAMETER_ID_SIZE, 0, sizeof(FwPrmIdType) + 1); + break; + case 3: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_VALUE_SIZE, 0, sizeof(U32) + 1); + break; + default: + FAIL() << "Reached unknown case"; + } + } + // Loop through failure statuses + for (FwSizeType i = 0; i < 2; i++) { + this->m_errorType = FILE_STATUS_ERROR; + // Set various file errors + switch (i) { + case 0: + this->m_status = Os::File::Status::DOESNT_EXIST; + break; + case 1: + this->m_status = Os::File::Status::NOT_OPENED; + break; + default: + FAIL() << "Reached unknown case"; + } + // Loop through various field reads + for (FwSizeType j = 0; j < 4; j++) { + clearEvents(); + this->m_waits = j; + this->m_impl.readParamFile(); + ASSERT_EVENTS_SIZE(1); + switch (j) { + case 0: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0,PrmReadError::DELIMITER, 0, this->m_status); + break; + case 1: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE, 0, this->m_status); + break; + case 2: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_ID, 0, this->m_status); + break; + case 3: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::PARAMETER_VALUE, 0, this->m_status); + break; + default: + FAIL() << "Reached unknown case"; + } + } + } + this->m_errorType = FILE_DATA_ERROR; + for (FwSizeType i = 0; i < 2; i++) { + clearEvents(); + this->m_waits = i; + this->m_impl.readParamFile(); + ASSERT_EVENTS_SIZE(1); + switch (i) { + case 0: + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + // Parameter read error caused by adding one to the expected read + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::DELIMITER_VALUE, 0, PRMDB_ENTRY_DELIMITER + 1); + break; + case 1: { + // Data in this test is corrupted by adding 1 to the first data byte read. Since data is stored in + // big-endian format the highest order byte of the record size (U32) must have one added to it. + // Expected result of '8' inherited from original design of test. + U32 expected_error_value = 8 + (1 << ((sizeof(U32) - 1) * 8)); + ASSERT_EVENTS_PrmFileReadError_SIZE(1); + ASSERT_EVENTS_PrmFileReadError(0, PrmReadError::RECORD_SIZE_VALUE, 0, expected_error_value); + break; + } + default: + FAIL() << "Reached unknown case"; + } + } } - void PrmDbImplTester::runFileWriteError() { + void PrmDbImplTester::runFileWriteError() { // File open error - this->clearEvents(); // register interceptor - Os::registerOpenInterceptor(this->OpenInterceptor,static_cast(this)); - this->m_testOpenStatus = Os::File::DOESNT_EXIST; + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::DOESNT_EXIST); // dispatch command this->sendCmd_PRM_SAVE_FILE(0,12); Fw::QueuedComponentBase::MsgDispatchStatus stat = this->m_impl.doDispatch(); @@ -588,290 +525,100 @@ void PrmDbImplTester::runFileReadError() { ASSERT_CMD_RESPONSE_SIZE(1); ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - Os::clearOpenInterceptor(); - - // Test delimiter write error - - // populate file again - this->runNominalPopulate(); - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // delimiter is first read - this->m_writesToWait = 0; - // set read status to bad - this->m_testWriteStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_writeTestType = FILE_WRITE_WRITE_ERROR; - - // send command to save file - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::DELIMITER,0,Os::File::NOT_OPENED); - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - Os::clearWriteInterceptor(); - - // Test delimiter write size error - - // populate file again this->runNominalPopulate(); - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // delimiter is first read - this->m_writesToWait = 0; - // set write status to okay - this->m_testWriteStatus = Os::File::OP_OK; - // set test type to read error - this->m_writeTestType = FILE_WRITE_SIZE_ERROR; - // set size to size of byte + 1 - this->m_writeSize = sizeof(U8)+1; - // send command to save file - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::DELIMITER_SIZE,0,sizeof(U8)+1); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - - Os::clearWriteInterceptor(); - - // Test record size write error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // size is second write - this->m_writesToWait = 1; - // set write status to bad - this->m_testWriteStatus = Os::File::NOT_OPENED; - // set test type to write error - this->m_writeTestType = FILE_WRITE_WRITE_ERROR; - // send command to save file - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::RECORD_SIZE,0,Os::File::NOT_OPENED); - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - Os::clearWriteInterceptor(); - - // Test record size write size error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // size is second read - this->m_writesToWait = 1; - // set read status to okay - this->m_testWriteStatus = Os::File::OP_OK; - // set test type to read error - this->m_writeTestType = FILE_WRITE_SIZE_ERROR; - // set size to size of U32 + 1 - this->m_writeSize = sizeof(U32)+1; - // send command to save file - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::RECORD_SIZE_SIZE,0,sizeof(U32)+1); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - - Os::clearWriteInterceptor(); - - // Test parameter ID write error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // ID is third read - this->m_writesToWait = 2; - // set write status to bad - this->m_testWriteStatus = Os::File::NOT_OPENED; - // set test type to write error - this->m_writeTestType = FILE_WRITE_WRITE_ERROR; - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::PARAMETER_ID,0,Os::File::NOT_OPENED); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - - Os::clearWriteInterceptor(); - - // Test parameter ID write size error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // size is second read - this->m_writesToWait = 2; - // set read status to okay - this->m_testWriteStatus = Os::File::OP_OK; - // set test type to read error - this->m_writeTestType = FILE_WRITE_SIZE_ERROR; - // set size to size of byte + 1 - this->m_writeSize = sizeof(FwPrmIdType)+1; - // send command to save file - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::PARAMETER_ID_SIZE,0,sizeof(FwPrmIdType)+1); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - - Os::clearWriteInterceptor(); - - // Test parameter value write error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // record is fourth read - this->m_writesToWait = 3; - // set read status to bad - this->m_testWriteStatus = Os::File::NOT_OPENED; - // set test type to read error - this->m_writeTestType = FILE_WRITE_WRITE_ERROR; - // send write command - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::PARAMETER_VALUE,0,Os::File::NOT_OPENED); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - Os::clearWriteInterceptor(); - - // Test parameter value write size error - - this->clearEvents(); - this->clearHistory(); - Os::registerWriteInterceptor(this->WriteInterceptor,static_cast(this)); - // record is fourth read - this->m_writesToWait = 3; - // set read status to okay - this->m_testWriteStatus = Os::File::OP_OK; - // set test type to read error - this->m_writeTestType = FILE_WRITE_SIZE_ERROR; - // set size to size of U32 + 1, which is nominal populate test value - this->m_writeSize = sizeof(U32)+1; - // send command - this->sendCmd_PRM_SAVE_FILE(0,12); - stat = this->m_impl.doDispatch(); - EXPECT_EQ(stat,Fw::QueuedComponentBase::MSG_DISPATCH_OK); - // check for failed event - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError_SIZE(1); - ASSERT_EVENTS_PrmFileWriteError(0,PrmWriteError::PARAMETER_VALUE_SIZE,0,sizeof(U32)+1); - - // check command status - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); - Os::clearWriteInterceptor(); - - } - - PrmDbImplTester::PrmDbImplTester(Svc::PrmDbImpl& inst) : - PrmDbGTestBase("testerbase",100), m_impl(inst) { - - } - - PrmDbImplTester::~PrmDbImplTester() { - } - - bool PrmDbImplTester::OpenInterceptor(Os::File::Status& stat, const char* fileName, Os::File::Mode mode, void* ptr) { - EXPECT_TRUE(ptr); - PrmDbImplTester* compPtr = static_cast(ptr); - stat = compPtr->m_testOpenStatus; - return false; - } - - bool PrmDbImplTester::ReadInterceptor(Os::File::Status& stat, void * buffer, NATIVE_INT_TYPE &size, bool waitForFull, void* ptr) { - EXPECT_TRUE(ptr); - PrmDbImplTester* compPtr = static_cast(ptr); - if (not compPtr->m_readsToWait--) { - // check test scenario - switch (compPtr->m_readTestType) { - case FILE_READ_READ_ERROR: - stat = compPtr->m_testReadStatus; + // Loop through all size errors testing each + Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK); + this->m_errorType = FILE_SIZE_ERROR; + for (FwSizeType i = 0; i < 4; i++) { + clearEvents(); + this->clearHistory(); + this->m_waits = i; + this->sendCmd_PRM_SAVE_FILE(0,12); + stat = this->m_impl.doDispatch(); + ASSERT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK); + ASSERT_EVENTS_SIZE(1); + switch (i) { + case 0: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::DELIMITER_SIZE, 0, sizeof(U8) + 1); break; - case FILE_READ_SIZE_ERROR: - size = compPtr->m_readSize; - stat = Os::File::OP_OK; + case 1: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::RECORD_SIZE_SIZE, 0, sizeof(U32) + 1); break; - case FILE_READ_DATA_ERROR: - memcpy(buffer,compPtr->m_readData,size); - stat = Os::File::OP_OK; + case 2: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_ID_SIZE, 0, sizeof(FwPrmIdType) + 1); break; - default: - EXPECT_TRUE(false); + case 3: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_VALUE_SIZE, 0, sizeof(U32) + 1); break; + default: + FAIL() << "Reached unknown case"; } - return false; - } else { - return true; + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); } - } - bool PrmDbImplTester::WriteInterceptor(Os::File::Status& stat, const void * buffer, NATIVE_INT_TYPE &size, bool waitForFull, void* ptr) { - EXPECT_TRUE(ptr); - PrmDbImplTester* compPtr = static_cast(ptr); - if (not compPtr->m_writesToWait--) { - // check test scenario - switch (compPtr->m_writeTestType) { - case FILE_WRITE_WRITE_ERROR: - stat = compPtr->m_testWriteStatus; + // Loop through failure statuses + for (FwSizeType i = 0; i < 2; i++) { + this->m_errorType = FILE_STATUS_ERROR; + // Set various file errors + switch (i) { + case 0: + this->m_status = Os::File::Status::DOESNT_EXIST; break; - case FILE_WRITE_SIZE_ERROR: - size = compPtr->m_writeSize; - stat = Os::File::OP_OK; + case 1: + this->m_status = Os::File::Status::NOT_OPENED; break; default: - EXPECT_TRUE(false); - break; + FAIL() << "Reached unknown case"; + } + // Loop through various field reads + for (FwSizeType j = 0; j < 4; j++) { + clearEvents(); + this->clearHistory(); + this->m_waits = j; + this->sendCmd_PRM_SAVE_FILE(0,12); + stat = this->m_impl.doDispatch(); + ASSERT_EQ(stat, Fw::QueuedComponentBase::MSG_DISPATCH_OK); + ASSERT_EVENTS_SIZE(1); + switch (j) { + case 0: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::DELIMITER, 0, this->m_status); + break; + case 1: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::RECORD_SIZE, 0, this->m_status); + break; + case 2: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_ID, 0, this->m_status); + break; + case 3: + ASSERT_EVENTS_PrmFileWriteError_SIZE(1); + ASSERT_EVENTS_PrmFileWriteError(0, PrmWriteError::PARAMETER_VALUE, 0, this->m_status); + break; + default: + FAIL() << "Reached unknown case"; + } + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0,PrmDbImpl::OPCODE_PRM_SAVE_FILE,12,Fw::CmdResponse::EXECUTION_ERROR); } - return false; - } else { - return true; } } + + PrmDbImplTester::PrmDbImplTester(Svc::PrmDbImpl& inst) : + PrmDbGTestBase("testerbase",100), m_impl(inst) { + PrmDbImplTester::PrmDbTestFile::setTester(this); + } + + PrmDbImplTester::~PrmDbImplTester() { + } + void PrmDbImplTester :: from_pingOut_handler( const NATIVE_INT_TYPE portNum, @@ -880,5 +627,27 @@ void PrmDbImplTester::runFileReadError() { { this->pushFromPortEntry_pingOut(key); } - -} /* namespace SvcTest */ +} /* namespace Svc */ + +namespace Os { +//! Overrides the default delegate function with this one as it is defined in the local compilation archive +//! \param aligned_placement_new_memory: memory to fill +//! \param to_copy: possible copy +//! \return: new interceptor +FileInterface *FileInterface::getDelegate(U8 *aligned_placement_new_memory, const FileInterface* to_copy) { + FW_ASSERT(aligned_placement_new_memory != nullptr); + const Svc::PrmDbImplTester::PrmDbTestFile* copy_me = + reinterpret_cast(to_copy); + // Placement-new the file handle into the opaque file-handle storage + static_assert(sizeof(Svc::PrmDbImplTester::PrmDbTestFile) <= FW_HANDLE_MAX_SIZE, "Handle size not large enough"); + static_assert((FW_HANDLE_ALIGNMENT % alignof(Svc::PrmDbImplTester::PrmDbTestFile)) == 0, "Handle alignment invalid"); + Svc::PrmDbImplTester::PrmDbTestFile *interface = nullptr; + if (to_copy == nullptr) { + interface = new(aligned_placement_new_memory) Svc::PrmDbImplTester::PrmDbTestFile; + } else { + interface = new(aligned_placement_new_memory) Svc::PrmDbImplTester::PrmDbTestFile(*copy_me); + } + FW_ASSERT(interface != nullptr); + return interface; +} +} diff --git a/Svc/PrmDb/test/ut/PrmDbImplTester.hpp b/Svc/PrmDb/test/ut/PrmDbImplTester.hpp index c350745dc6..d668fa0d09 100644 --- a/Svc/PrmDb/test/ut/PrmDbImplTester.hpp +++ b/Svc/PrmDb/test/ut/PrmDbImplTester.hpp @@ -11,11 +11,11 @@ #include #include #include -#include +#include namespace Svc { - class PrmDbImplTester: public PrmDbGTestBase { + class PrmDbImplTester : public PrmDbGTestBase { public: PrmDbImplTester(Svc::PrmDbImpl& inst); virtual ~PrmDbImplTester(); @@ -41,40 +41,38 @@ namespace Svc { Svc::PrmDbImpl& m_impl; void resetEvents(); - // open call modifiers - static bool OpenInterceptor(Os::File::Status &stat, const char* fileName, Os::File::Mode mode, void* ptr); - Os::File::Status m_testOpenStatus; - // read call modifiers - - static bool ReadInterceptor(Os::File::Status &stat, void * buffer, NATIVE_INT_TYPE &size, bool waitForFull, void* ptr); - Os::File::Status m_testReadStatus; - // How many read calls to let pass before modifying - NATIVE_INT_TYPE m_readsToWait; // enumeration to tell what kind of error to inject - typedef enum { - FILE_READ_READ_ERROR, // return a bad read status - FILE_READ_SIZE_ERROR, // return a bad size - FILE_READ_DATA_ERROR // return unexpected data - } FileReadTestType; - FileReadTestType m_readTestType; - NATIVE_INT_TYPE m_readSize; - BYTE m_readData[PRMDB_IMPL_TESTER_MAX_READ_BUFFER]; + enum ErrorType { + FILE_STATUS_ERROR, // return a bad read status + FILE_SIZE_ERROR, // return a bad size + FILE_DATA_ERROR, // return unexpected data + FILE_READ_NO_ERROR, // No error + }; + Os::File::Status m_status; + FwSizeType m_waits = 0; + ErrorType m_errorType = FILE_READ_NO_ERROR; + + BYTE m_io_data[PRMDB_IMPL_TESTER_MAX_READ_BUFFER]; // write call modifiers - static bool WriteInterceptor(Os::File::Status &status, const void * buffer, NATIVE_INT_TYPE &size, bool waitForDone, void* ptr); + Os::File::Status WriteInterceptor(); Os::File::Status m_testWriteStatus; - // How many read calls to let pass before modifying - NATIVE_INT_TYPE m_writesToWait; - // enumeration to tell what kind of error to inject - typedef enum { - FILE_WRITE_WRITE_ERROR, // return a bad read status - FILE_WRITE_SIZE_ERROR, // return a bad size - } FileWriteTestType; - FileWriteTestType m_writeTestType; - NATIVE_INT_TYPE m_writeSize; + + public: + class PrmDbTestFile : public Os::Stub::File::Test::TestFile { + public: + Status read(U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + Status write(const U8 *buffer, FwSignedSizeType &size, WaitType wait) override; + + // Tracks the current tester + static void setTester(PrmDbImplTester* tester); + static PrmDbImplTester* s_tester; + + }; }; diff --git a/Utils/CRCChecker.cpp b/Utils/CRCChecker.cpp index e5ba06e444..eab300d86e 100644 --- a/Utils/CRCChecker.cpp +++ b/Utils/CRCChecker.cpp @@ -23,21 +23,21 @@ namespace Utils { { FW_ASSERT(fname != nullptr); - NATIVE_INT_TYPE i; - NATIVE_INT_TYPE blocks; - NATIVE_INT_TYPE remaining_bytes; - FwSizeType filesize; + FwSignedSizeType i; + FwSignedSizeType blocks; + FwSignedSizeType remaining_bytes; + FwSignedSizeType filesize; Os::File f; Os::FileSystem::Status fs_stat; Os::File::Status stat; Utils::Hash hash; U32 checksum; I32 s_stat; - NATIVE_INT_TYPE int_file_size; - NATIVE_INT_TYPE bytes_to_read; - NATIVE_INT_TYPE bytes_to_write; - char hashFilename[CRC_MAX_FILENAME_SIZE]; - char block_data[CRC_FILE_READ_BLOCK]; + FwSignedSizeType int_file_size; + FwSignedSizeType bytes_to_read; + FwSignedSizeType bytes_to_write; + CHAR hashFilename[CRC_MAX_FILENAME_SIZE]; + U8 block_data[CRC_FILE_READ_BLOCK]; fs_stat = Os::FileSystem::getFileSize(fname, filesize); if(fs_stat != Os::FileSystem::OP_OK) @@ -45,11 +45,7 @@ namespace Utils { return FAILED_FILE_SIZE; } - int_file_size = static_cast(filesize); - if(static_cast(int_file_size) != filesize) - { - return FAILED_FILE_SIZE_CAST; - } + int_file_size = filesize; // Open file stat = f.open(fname, Os::File::OPEN_READ); @@ -134,7 +130,7 @@ namespace Utils { } // Read checksum file - NATIVE_INT_TYPE checksum_from_file_size = sizeof(checksum_from_file); + FwSignedSizeType checksum_from_file_size = static_cast(sizeof(checksum_from_file)); stat = f.read(reinterpret_cast(&checksum_from_file), checksum_from_file_size); if(stat != Os::File::OP_OK || checksum_from_file_size != sizeof(checksum_from_file)) { @@ -151,19 +147,19 @@ namespace Utils { { FW_ASSERT(fname != nullptr); - NATIVE_INT_TYPE i; - NATIVE_INT_TYPE blocks; - NATIVE_INT_TYPE remaining_bytes; - FwSizeType filesize; + FwSignedSizeType i; + FwSignedSizeType blocks; + PlatformIntType remaining_bytes; + FwSignedSizeType filesize; Os::File f; Os::FileSystem::Status fs_stat; Os::File::Status stat; Utils::Hash hash; U32 checksum; U32 checksum_from_file; - NATIVE_INT_TYPE int_file_size; - NATIVE_INT_TYPE bytes_to_read; - char block_data[CRC_FILE_READ_BLOCK]; + FwSignedSizeType int_file_size; + FwSignedSizeType bytes_to_read; + U8 block_data[CRC_FILE_READ_BLOCK]; fs_stat = Os::FileSystem::getFileSize(fname, filesize); if(fs_stat != Os::FileSystem::OP_OK) @@ -172,7 +168,7 @@ namespace Utils { } int_file_size = static_cast(filesize); - if(static_cast(int_file_size) != filesize) + if(static_cast(int_file_size) != filesize) { return FAILED_FILE_SIZE_CAST; } @@ -186,7 +182,7 @@ namespace Utils { // Read file bytes_to_read = CRC_FILE_READ_BLOCK; - blocks = int_file_size / CRC_FILE_READ_BLOCK; + blocks = filesize / CRC_FILE_READ_BLOCK; for(i = 0; i < blocks; i++) { stat = f.read(block_data, bytes_to_read); diff --git a/cmake/API.cmake b/cmake/API.cmake index 5d2c5e2380..f0788241d2 100644 --- a/cmake/API.cmake +++ b/cmake/API.cmake @@ -560,6 +560,20 @@ macro(register_fprime_build_autocoder TARGET_FILE_PATH) register_fprime_list_helper("${TARGET_FILE_PATH}" FPRIME_AUTOCODER_TARGET_LIST) endmacro(register_fprime_build_autocoder) +#### +# Function `create_implementation_interface`: +# +# Helper function to create implementation interface library once and only once to ensure it exists. +# +# **IMPLEMENTATION**: implementation library name (resolved) +#### +function (create_implementation_interface IMPLEMENTATION) + if (TARGET "${IMPLEMENTATION}") + return() + endif() + add_library("${IMPLEMENTATION}" INTERFACE) +endfunction() + #### # Function `require_fprime_implementation`: @@ -569,11 +583,23 @@ endmacro(register_fprime_build_autocoder) # `choose_fprime_implementation` in the platform and may be overridden in in the executable/deployment. # # **IMPLEMENTATION:** implementation module name that must be covered +# **REQUESTER:** (optional) the requester of the implementation. Default: ${FPRIME_CURRENT_MODULE} #### function(require_fprime_implementation IMPLEMENTATION) + if (ARGC EQUAL 2) + set(REQUESTER "${ARGV1}") + elseif (FPRIME_CURRENT_MODULE) + set(REQUESTER "${FPRIME_CURRENT_MODULE}") + else () + message(FATAL_ERROR "Cannot determine current module, please supply as second argument") + endif() resolve_dependencies(IMPLEMENTATION "${IMPLEMENTATION}") + resolve_dependencies(REQUESTER "${REQUESTER}") + + create_implementation_interface("${IMPLEMENTATION}") append_list_property("${IMPLEMENTATION}" GLOBAL PROPERTY "REQUIRED_IMPLEMENTATIONS") - append_list_property("${FPRIME_CURRENT_MODULE}" GLOBAL PROPERTY "${IMPLEMENTATION}_REQUESTERS") + append_list_property("${FPRIME_CURRENT_MODULE}" TARGET "${IMPLEMENTATION}" PROPERTY "REQUESTERS") + add_dependencies("${REQUESTER}" "${IMPLEMENTATION}") endfunction() #### @@ -589,8 +615,12 @@ endfunction() function(register_fprime_implementation IMPLEMENTATION IMPLEMENTOR) resolve_dependencies(IMPLEMENTATION "${IMPLEMENTATION}") resolve_dependencies(IMPLEMENTOR "${IMPLEMENTOR}") - append_list_property("${IMPLEMENTOR}" GLOBAL PROPERTY "${IMPLEMENTATION}_IMPLEMENTORS") + create_implementation_interface("${IMPLEMENTATION}") + append_list_property("${IMPLEMENTOR}" TARGET "${IMPLEMENTATION}" PROPERTY "IMPLEMENTORS") + append_list_property("${ARGN}" TARGET "${IMPLEMENTOR}" PROPERTY "REQUIRED_SOURCE_FILES") + add_dependencies("${IMPLEMENTATION}" "${IMPLEMENTOR}") endfunction() + #### # Function `choose_fprime_implementation`: # @@ -614,9 +644,10 @@ function(choose_fprime_implementation IMPLEMENTATION IMPLEMENTOR) else() message(FATAL_ERROR "Cannot call 'choose_fprime_implementation' outside an fprime module or platform CMake file") endif() - set_property(GLOBAL PROPERTY "${IMPLEMENTATION}_${ACTIVE_MODULE}" "${IMPLEMENTOR}") + create_implementation_interface("${IMPLEMENTATION}") + # Add this implementation in the case it has not been added append_list_property("${IMPLEMENTATION}" GLOBAL PROPERTY "REQUIRED_IMPLEMENTATIONS") - append_list_property("${IMPLEMENTOR}" GLOBAL PROPERTY "${IMPLEMENTATION}_IMPLEMENTORS") + set_property(GLOBAL PROPERTY "${ACTIVE_MODULE}_${IMPLEMENTATION}" "${IMPLEMENTOR}") endfunction() #### Documentation links diff --git a/cmake/implementation.cmake b/cmake/implementation.cmake index 2e74fccbb1..bac00d0a77 100644 --- a/cmake/implementation.cmake +++ b/cmake/implementation.cmake @@ -35,12 +35,12 @@ include_guard() #### function(remap_implementation_choices INFERRED DECLARED) # Check and setup implementors - get_property(LOCAL_REQUIRED GLOBAL PROPERTY "REQUIRED_IMPLEMENTATIONS") - if (LOCAL_REQUIRED) - foreach (IMPLEMENTATION IN LISTS LOCAL_REQUIRED) - get_property(IMPLEMENTOR GLOBAL PROPERTY "${IMPLEMENTATION}_${INFERRED}") + get_property(GLOBAL_REQUIRED GLOBAL PROPERTY "REQUIRED_IMPLEMENTATIONS") + if (GLOBAL_REQUIRED) + foreach (IMPLEMENTATION IN LISTS GLOBAL_REQUIRED) + get_property(IMPLEMENTOR GLOBAL PROPERTY "${INFERRED}_${IMPLEMENTATION}") if (IMPLEMENTOR) - set_property(GLOBAL PROPERTY "${IMPLEMENTATION}_${DECLARED}" "${IMPLEMENTOR}") + set_property(GLOBAL PROPERTY "${DECLARED}_${IMPLEMENTATION}" "${IMPLEMENTOR}") endif() endforeach() endif () @@ -59,10 +59,15 @@ function(setup_executable_implementations MODULE) # Check and setup implementors get_property(LOCAL_REQUIRED GLOBAL PROPERTY "REQUIRED_IMPLEMENTATIONS") if (LOCAL_REQUIRED) + init_variables(IMPLEMENTATION_SOURCES IMPLEMENTATION_DEPENDENCIES) foreach (IMPLEMENTATION IN LISTS LOCAL_REQUIRED) setup_executable_implementation("${IMPLEMENTATION}" "${MODULE}") + list(APPEND IMPLEMENTATION_SOURCES ${NEW_IMPLEMENTATION_SOURCES}) + list(APPEND IMPLEMENTATION_DEPENDENCIES ${NEW_IMPLEMENTATION_DEPENDENCIES}) endforeach () endif () + set(IMPLEMENTATION_SOURCES "${IMPLEMENTATION_SOURCES}" PARENT_SCOPE) + set(IMPLEMENTATION_DEPENDENCIES "${IMPLEMENTATION_DEPENDENCIES}" PARENT_SCOPE) endfunction() #### @@ -76,16 +81,11 @@ endfunction() ##### function(setup_executable_implementation IMPLEMENTATION MODULE) # Get the chosen implementor and fallback to the platform choice - get_property(IMPLEMENTOR GLOBAL PROPERTY "${IMPLEMENTATION}_${MODULE}") + get_property(IMPLEMENTOR GLOBAL PROPERTY "${MODULE}_${IMPLEMENTATION}") if (NOT IMPLEMENTOR) - get_property(IMPLEMENTOR GLOBAL PROPERTY "${IMPLEMENTATION}_${FPRIME_PLATFORM}") + get_property(IMPLEMENTOR GLOBAL PROPERTY "${FPRIME_PLATFORM}_${IMPLEMENTATION}") endif() # Handle a failure to choose anything - get_property(REQUESTERS GLOBAL PROPERTY "${IMPLEMENTATION}_REQUESTERS") - if (NOT REQUESTERS) - set(REQUESTERS) - endif () - string(REPLACE ";" ", " REQUESTERS_STRING "${REQUESTERS}") if (NOT IMPLEMENTOR) get_property(LOCAL_IMPLEMENTATIONS GLOBAL PROPERTY "${IMPLEMENTATION}_IMPLEMENTORS") if (NOT LOCAL_IMPLEMENTATIONS) @@ -93,11 +93,16 @@ function(setup_executable_implementation IMPLEMENTATION MODULE) endif () string(REPLACE ";" ", " POSSIBLE "${LOCAL_IMPLEMENTATIONS}") message(FATAL_ERROR "An implementation of ${IMPLEMENTATION} is required. Choose from: ${POSSIBLE}") + elseif (IMPLEMENTOR STREQUAL "${IMPLEMENTATION}_None") + if (CMAKE_DEBUG_OUTPUT) + message(STATUS "[${MODULE}] Declining Implementation of ${IMPLEMENTATION}") + endif () + return() endif () if (CMAKE_DEBUG_OUTPUT) - message(STATUS "Using Implementation: ${IMPLEMENTOR} for ${IMPLEMENTATION}") + message(STATUS "[${MODULE}] Using Implementation: ${IMPLEMENTOR} for ${IMPLEMENTATION}") endif() - # Replicated order to solve the circular dependency issue without causing a hard-dependency - target_link_libraries("${MODULE}" PUBLIC ${REQUESTERS} "${IMPLEMENTOR}" ${REQUESTERS}) - add_dependencies("${MODULE}" "${IMPLEMENTOR}") + target_sources("${MODULE}" PRIVATE "$") + target_link_libraries("${MODULE}" PRIVATE ${IMPLEMENTOR}) + add_dependencies("${MODULE}" ${IMPLEMENTOR}) endfunction() diff --git a/cmake/module.cmake b/cmake/module.cmake index 656361bcd5..e8ba77b41d 100644 --- a/cmake/module.cmake +++ b/cmake/module.cmake @@ -35,12 +35,6 @@ function(generate_base_module_properties TARGET_TYPE TARGET_NAME SOURCE_FILES DE message(FATAL_ERROR "Module ${FPRIME_CURRENT_MODULE} cannot register object of type ${TARGET_TYPE}") endif() - # Handle updates when the types have diverged - if (NOT TARGET_NAME STREQUAL "${FPRIME_CURRENT_MODULE}") - # Update implementation choices - remap_implementation_choices("${FPRIME_CURRENT_MODULE}" "${TARGET_NAME}") - endif() - # Modules properties for posterity set_target_properties("${TARGET_NAME}" PROPERTIES FP_TYPE "${TARGET_TYPE}") set_property(GLOBAL APPEND PROPERTY FPRIME_MODULES "${TARGET_NAME}") diff --git a/cmake/platform/Darwin.cmake b/cmake/platform/Darwin.cmake index 35a6a8c4da..2cf1e88655 100644 --- a/cmake/platform/Darwin.cmake +++ b/cmake/platform/Darwin.cmake @@ -18,6 +18,7 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER) message(STATUS "Requiring thread library") FIND_PACKAGE ( Threads REQUIRED ) endif() +choose_fprime_implementation(Os/File Os/File/Posix) # Add linux include path which is compatible with Darwin for PlatformTypes.hpp include_directories(SYSTEM "${CMAKE_CURRENT_LIST_DIR}/types") diff --git a/cmake/platform/Linux.cmake b/cmake/platform/Linux.cmake index aef6fea3e4..286c842012 100644 --- a/cmake/platform/Linux.cmake +++ b/cmake/platform/Linux.cmake @@ -9,6 +9,7 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER) message(STATUS "Requiring thread library") FIND_PACKAGE ( Threads REQUIRED ) endif() +choose_fprime_implementation(Os/File Os/File/Posix) # Use common linux setup add_definitions(-DTGT_OS_TYPE_LINUX) diff --git a/cmake/platform/types/PlatformTypes.h b/cmake/platform/types/PlatformTypes.h index 5e78a91122..725ce9528a 100644 --- a/cmake/platform/types/PlatformTypes.h +++ b/cmake/platform/types/PlatformTypes.h @@ -47,8 +47,11 @@ typedef unsigned int PlatformUIntType; typedef PlatformIntType PlatformIndexType; #define PRI_PlatformIndexType PRI_PlatformIntType -typedef PlatformUIntType PlatformSizeType; -#define PRI_PlatformSizeType PRI_PlatformUIntType +typedef int64_t PlatformSignedSizeType; +#define PRI_PlatformSignedSizeType PRId64 + +typedef uint64_t PlatformSizeType; +#define PRI_PlatformSizeType PRIu64 typedef PlatformIntType PlatformAssertArgType; #define PRI_PlatformAssertArgType PRI_PlatformIntType diff --git a/cmake/target/build.cmake b/cmake/target/build.cmake index 4aed885080..dac8a1715a 100644 --- a/cmake/target/build.cmake +++ b/cmake/target/build.cmake @@ -62,15 +62,6 @@ function(build_setup_build_module MODULE SOURCES GENERATED DEPENDENCIES) set_assert_flags("${SRC_FILE}") endforeach() endif() - # Includes the source, so that the Ac files can include source headers - #target_include_directories("${MODULE}" PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - - - # Handle executable items' need for determined package implementation choices - is_target_library(IS_LIB "${MODULE}") - if (NOT IS_LIB) - setup_executable_implementations("${MODULE}") - endif () # For every detected dependency, add them to the supplied module. This enforces build order. # Also set the link dependencies on this module. CMake rolls-up link dependencies, and thus @@ -88,11 +79,27 @@ function(build_setup_build_module MODULE SOURCES GENERATED DEPENDENCIES) # # 1. Targets that will exist, but do not exist at the time of this call will be assumed to be a library # 2. EXECUTABLE and UTILITY targets can only be added to MOD_DEPS when they are pre-defined - is_target_library(IS_LIB "${DEPENDENCY}") - if (LINKER_ONLY OR NOT TARGET "${DEPENDENCY}" OR IS_LIB) + is_target_library(IS_LIB_DEP "${DEPENDENCY}") + if (LINKER_ONLY OR NOT TARGET "${DEPENDENCY}" OR IS_LIB_DEP) target_link_libraries(${MODULE} PUBLIC "${DEPENDENCY}") endif() endforeach() + + + # Extra source files, dependencies, and link libraries need to be added to executables to account for the chosen + # implementations. First, for modules whose names differ from FPRIME_CURRENT_MODULE the chosen implementation is + # remapped to them. Then the implementation set are calculated and sources, link libraries and dependencies added. + is_target_library(IS_LIB "${MODULE}") + if (NOT IS_LIB) + # Handle updates when the types have diverged + if (NOT MODULE STREQUAL "${FPRIME_CURRENT_MODULE}") + # Update implementation choices + remap_implementation_choices("${FPRIME_CURRENT_MODULE}" "${MODULE}") + endif() + setup_executable_implementations("${MODULE}") + endif () + + set_property(TARGET "${MODULE}" PROPERTY FPRIME_TARGET_DEPENDENCIES ${TARGET_DEPENDENCIES}) # Special flags applied to modules when compiling with testing enabled if (BUILD_TESTING) diff --git a/cmake/test/data/test-implementations/Deployment/CMakeLists.txt b/cmake/test/data/test-implementations/Deployment/CMakeLists.txt index 6f49fd7a72..35cd9fc143 100644 --- a/cmake/test/data/test-implementations/Deployment/CMakeLists.txt +++ b/cmake/test/data/test-implementations/Deployment/CMakeLists.txt @@ -16,6 +16,7 @@ include("${FPRIME_FRAMEWORK_PATH}/cmake/FPrime-Code.cmake") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TestModule") set(FPRIME_CURRENT_MODULE Deployment) set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Main.cpp") -set(MOD_DEPS Deployment/TestModule) +set(MOD_DEPS Fw_Types Deployment/TestModule) choose_fprime_implementation(Test/Override Test/Override/Override) # Choose an override +choose_fprime_implementation(Os/File Os/File/Posix) register_fprime_executable() diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake index a9caaa52bd..061a4b237b 100644 --- a/cmake/utilities.cmake +++ b/cmake/utilities.cmake @@ -602,7 +602,7 @@ endfunction() #### function(append_list_property NEW_ITEM) get_property(LOCAL_COPY ${ARGN}) - list(APPEND LOCAL_COPY "${NEW_ITEM}") + list(APPEND LOCAL_COPY ${NEW_ITEM}) list(REMOVE_DUPLICATES LOCAL_COPY) set_property(${ARGN} "${LOCAL_COPY}") endfunction() diff --git a/config/FpConfig.h b/config/FpConfig.h index 36c7173fcb..a1b9a95856 100644 --- a/config/FpConfig.h +++ b/config/FpConfig.h @@ -15,6 +15,9 @@ typedef PlatformIndexType FwIndexType; #define PRI_FwIndexType PRI_PlatformIndexType +typedef PlatformSignedSizeType FwSignedSizeType; +#define PRI_FwSignedSizeType PRI_PlatformSignedSizeType + typedef PlatformSizeType FwSizeType; #define PRI_FwSizeType PRI_PlatformSizeType @@ -27,8 +30,8 @@ typedef PlatformIntType FwNativeIntType; typedef PlatformUIntType FwNativeUIntType; #define PRI_FwNativeUIntType PRI_PlatformUIntType -typedef U16 FwBuffSizeType; -#define PRI_FwBuffSizeType PRIu16 +typedef U16 FwSizeStoreType; +#define PRI_FwSizeStoreType PRIu16 typedef I32 FwEnumStoreType; #define PRI_FwEnumStoreType PRId32 @@ -344,7 +347,23 @@ typedef U32 FwDpPriorityType; #define FW_FIXED_LENGTH_STRING_SIZE 256 //!< Character array size for the filepath character type #endif +#ifndef FW_HANDLE_MAX_SIZE +#define FW_HANDLE_MAX_SIZE 16 //!< Maximum size of a handle for OS resources (files, queues, locks, etc.) +#endif + +#ifndef FW_HANDLE_ALIGNMENT +#define FW_HANDLE_ALIGNMENT 16 //!< Alignment of handle storage +#endif + +#ifndef FW_FILE_CHUNK_SIZE +#define FW_FILE_CHUNK_SIZE 512 //!< Chunk size for working with files +#endif + + // *** NOTE configuration checks are in Fw/Cfg/ConfigCheck.cpp in order to have // the type definitions in Fw/Types/BasicTypes available. +// DO NOT TOUCH. These types are specified for backwards naming compatibility. +typedef FwSizeStoreType FwBuffSizeType; +#define PRI_FwBuffSizeType PRI_FwSizeStoreType #endif