diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 111542f0db..75f73d63e8 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -6,3 +6,5 @@ usec usecs workaround workarounds +DEVICESM +HACKSM diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index a55382cce5..ccdb4d282e 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -152,6 +152,7 @@ CMDPACKET CMDREG cmds CMDSEQ +cmdsequencer cnt cntx cobj @@ -200,6 +201,7 @@ CRCs crcstat CREATEDIRECTORY Crosscompiling +crsmith crt CRTSCTS cryptsoft @@ -548,6 +550,7 @@ lammertbies LASTLOG LBLOCK LCHILD +leisher lemstarch lestarch levelname @@ -1212,4 +1215,5 @@ xsh xsltproc xxxx yacgen +zimri zmq \ No newline at end of file diff --git a/.github/actions/spelling/line_forbidden.patterns b/.github/actions/spelling/line_forbidden.patterns index e1272debd3..9e63f65613 100644 --- a/.github/actions/spelling/line_forbidden.patterns +++ b/.github/actions/spelling/line_forbidden.patterns @@ -11,9 +11,6 @@ # s.b. anymore \bany more[,.] -# s.b. cannot -\b[Cc]an not\b - # s.b. GitHub (?(timeout_microseconds)); FW_ASSERT(this->isValidPort(port)); FW_ASSERT(hostname != nullptr); - this->m_lock.lock(); this->m_timeoutSeconds = timeout_seconds; this->m_timeoutMicroseconds = timeout_microseconds; this->m_port = port; (void) Fw::StringUtils::string_copy(this->m_hostname, hostname, static_cast(SOCKET_MAX_HOSTNAME_SIZE)); - this->m_lock.unlock(); return SOCK_SUCCESS; } @@ -105,88 +103,44 @@ SocketIpStatus IpSocket::addressToIp4(const char* address, void* ip4) { return SOCK_SUCCESS; } -bool IpSocket::isStarted() { - bool is_started = false; - this->m_lock.lock(); - is_started = this->m_started; - this->m_lock.unLock(); - return is_started; +void IpSocket::close(NATIVE_INT_TYPE fd) { + (void)::shutdown(fd, SHUT_RDWR); + (void)::close(fd); } -bool IpSocket::isOpened() { - bool is_open = false; - this->m_lock.lock(); - is_open = this->m_open; - this->m_lock.unLock(); - return is_open; -} - -void IpSocket::close() { - this->m_lock.lock(); - if (this->m_fd != -1) { - (void)::shutdown(this->m_fd, SHUT_RDWR); - (void)::close(this->m_fd); - this->m_fd = -1; - } - this->m_open = false; - this->m_lock.unLock(); -} - -void IpSocket::shutdown() { - this->close(); - this->m_lock.lock(); - this->m_started = false; - this->m_lock.unLock(); +void IpSocket::shutdown(NATIVE_INT_TYPE fd) { + this->close(fd); } SocketIpStatus IpSocket::startup() { - this->m_lock.lock(); - this->m_started = true; - this->m_lock.unLock(); + // no op for non-server components return SOCK_SUCCESS; } -SocketIpStatus IpSocket::open() { - NATIVE_INT_TYPE fd = -1; +SocketIpStatus IpSocket::open(NATIVE_INT_TYPE& fd) { SocketIpStatus status = SOCK_SUCCESS; - this->m_lock.lock(); - FW_ASSERT(m_fd == -1 and not m_open); // Ensure we are not opening an opened socket - this->m_lock.unlock(); // Open a TCP socket for incoming commands, and outgoing data if not using UDP status = this->openProtocol(fd); if (status != SOCK_SUCCESS) { - FW_ASSERT(m_fd == -1); // Ensure we properly kept closed on error + FW_ASSERT(fd == -1); // Ensure we properly kept closed on error return status; } - // Lock to update values and "officially open" - this->m_lock.lock(); - this->m_fd = fd; - this->m_open = true; - this->m_lock.unLock(); return status; } -SocketIpStatus IpSocket::send(const U8* const data, const U32 size) { +SocketIpStatus IpSocket::send(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) { U32 total = 0; I32 sent = 0; - this->m_lock.lock(); - NATIVE_INT_TYPE fd = this->m_fd; - this->m_lock.unlock(); - // Prevent transmission before connection, or after a disconnect - if (fd == -1) { - return SOCK_DISCONNECTED; - } // Attempt to send out data and retry as necessary for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) { // Send using my specific protocol - sent = this->sendProtocol(data + total, size - total); + sent = this->sendProtocol(fd, data + total, size - total); // Error is EINTR or timeout just try again if (((sent == -1) && (errno == EINTR)) || (sent == 0)) { continue; } // Error bad file descriptor is a close along with reset else if ((sent == -1) && ((errno == EBADF) || (errno == ECONNRESET))) { - this->close(); return SOCK_DISCONNECTED; } // Error returned, and it wasn't an interrupt nor a disconnect @@ -205,21 +159,15 @@ SocketIpStatus IpSocket::send(const U8* const data, const U32 size) { return SOCK_SUCCESS; } -SocketIpStatus IpSocket::recv(U8* data, U32& req_read) { +SocketIpStatus IpSocket::recv(NATIVE_INT_TYPE fd, U8* data, U32& req_read) { I32 size = 0; - // Check for previously disconnected socket - this->m_lock.lock(); - NATIVE_INT_TYPE fd = this->m_fd; - this->m_lock.unlock(); - if (fd == -1) { - return SOCK_DISCONNECTED; - } // Try to read until we fail to receive data for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (size <= 0); i++) { // Attempt to recv out data size = this->recvProtocol(data, req_read); + // Nothing to be received if ((size == -1) && (errno == EAGAIN)) { req_read = 0; return SOCK_SUCCESS; @@ -230,7 +178,6 @@ SocketIpStatus IpSocket::recv(U8* data, U32& req_read) { } // Zero bytes read reset or bad ef means we've disconnected else if (size == 0 || ((size == -1) && ((errno == ECONNRESET) || (errno == EBADF)))) { - this->close(); req_read = static_cast(size); return SOCK_DISCONNECTED; } diff --git a/Drv/Ip/IpSocket.hpp b/Drv/Ip/IpSocket.hpp index 0adfdc827b..d427bc719e 100644 --- a/Drv/Ip/IpSocket.hpp +++ b/Drv/Ip/IpSocket.hpp @@ -68,24 +68,6 @@ class IpSocket { */ SocketIpStatus configure(const char* hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds); - /** - * \brief Returns true when the socket is started - * - * Returns true when the socket is started up sufficiently to be actively listening to clients. Returns false - * otherwise. This means `startup()` was called and returned success. - */ - bool isStarted(); - - /** - * \brief check if IP socket has previously been opened - * - * Check if this IpSocket has previously been opened. In the case of Udp this will check for outgoing transmissions - * and (if configured) incoming transmissions as well. This does not guarantee errors will not occur when using this - * socket as the remote component may have disconnected. - * - * \return true if socket is open, false otherwise - */ - bool isOpened(); /** * \brief startup the socket, a no-op on unless this is server @@ -112,9 +94,10 @@ class IpSocket { * * Note: delegates to openProtocol for protocol specific implementation * + * \param fd: file descriptor to open * \return status of open */ - SocketIpStatus open(); + SocketIpStatus open(NATIVE_INT_TYPE& fd); /** * \brief send data out the IP socket from the given buffer * @@ -126,11 +109,12 @@ class IpSocket { * * Note: delegates to `sendProtocol` to send the data * + * \param fd: file descriptor to send to * \param data: pointer to data to send * \param size: size of data to send * \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error */ - SocketIpStatus send(const U8* const data, const U32 size); + SocketIpStatus send(NATIVE_INT_TYPE fd, const U8* const data, const U32 size); /** * \brief receive data from the IP socket from the given buffer * @@ -142,26 +126,31 @@ class IpSocket { * * Note: delegates to `recvProtocol` to send the data * + * \param fd: file descriptor to recv from * \param data: pointer to data to fill with received data * \param size: maximum size of data buffer to fill * \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error */ - SocketIpStatus recv(U8* const data, U32& size); + SocketIpStatus recv(NATIVE_INT_TYPE fd, U8* const data, U32& size); /** * \brief closes the socket * * Closes the socket opened by the open call. In this case of the TcpServer, this does NOT close server's listening * port (call `shutdown`) but will close the active client connection. + * + * \param fd: file descriptor to close */ - void close(); + void close(NATIVE_INT_TYPE fd); /** * \brief shutdown the socket * * Closes the socket opened by the open call. In this case of the TcpServer, this does close server's listening * port. This will shutdown all clients. + * + * \param fd: file descriptor to shutdown */ - virtual void shutdown(); + virtual void shutdown(NATIVE_INT_TYPE fd); PROTECTED: /** @@ -198,27 +187,25 @@ class IpSocket { virtual SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) = 0; /** * \brief Protocol specific implementation of send. Called directly with retry from send. + * \param fd: file descriptor to send to * \param data: data to send * \param size: size of data to send * \return: size of data sent, or -1 on error. */ - virtual I32 sendProtocol(const U8* const data, const U32 size) = 0; + virtual I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) = 0; /** * \brief Protocol specific implementation of recv. Called directly with error handling from recv. + * \param fd: file descriptor to recv from * \param data: data pointer to fill * \param size: size of data buffer * \return: size of data received, or -1 on error. */ - virtual I32 recvProtocol( U8* const data, const U32 size) = 0; + virtual I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) = 0; - Os::Mutex m_lock; - NATIVE_INT_TYPE m_fd; U32 m_timeoutSeconds; U32 m_timeoutMicroseconds; U16 m_port; //!< IP address port used - bool m_open; //!< Have we successfully opened - bool m_started; //!< Have we successfully started the socket char m_hostname[SOCKET_MAX_HOSTNAME_SIZE]; //!< Hostname to supply }; } // namespace Drv diff --git a/Drv/Ip/SocketComponentHelper.cpp b/Drv/Ip/SocketComponentHelper.cpp new file mode 100644 index 0000000000..ebd177fe43 --- /dev/null +++ b/Drv/Ip/SocketComponentHelper.cpp @@ -0,0 +1,202 @@ +// ====================================================================== +// \title SocketComponentHelper.cpp +// \author mstarch, crsmith +// \brief cpp file for SocketComponentHelper implementation class +// +// \copyright +// Copyright 2009-2020, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#include +#include +#include +#include + +#define MAXIMUM_SIZE 0x7FFFFFFF + +namespace Drv { + +SocketComponentHelper::SocketComponentHelper() : m_fd(-1), m_reconnect(false), m_stop(false), m_started(false), m_open(false) {} + +SocketComponentHelper::~SocketComponentHelper() {} + +void SocketComponentHelper::start(const Fw::StringBase &name, + const bool reconnect, + const Os::Task::ParamType priority, + const Os::Task::ParamType stack, + const Os::Task::ParamType cpuAffinity) { + FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times + FW_ASSERT(not this->m_stop); // It is a coding error to stop the thread before it is started + m_reconnect = reconnect; + // Note: the first step is for the IP socket to open the port + Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity); + Os::Task::Status stat = m_task.start(arguments); + FW_ASSERT(Os::Task::OP_OK == stat, static_cast(stat)); +} + +SocketIpStatus SocketComponentHelper::startup() { + this->m_started = true; + return this->getSocketHandler().startup(); +} + +bool SocketComponentHelper::isStarted() { + bool is_started = false; + is_started = this->m_started; + return is_started; +} + +SocketIpStatus SocketComponentHelper::open() { + SocketIpStatus status = SOCK_FAILED_TO_GET_SOCKET; + NATIVE_INT_TYPE fd = -1; + this->m_lock.lock(); + if (not this->m_open) { + FW_ASSERT(this->m_fd == -1 and not this->m_open); // Ensure we are not opening an opened socket + status = this->getSocketHandler().open(fd); + this->m_fd = fd; + // Call connected any time the open is successful + if (Drv::SOCK_SUCCESS == status) { + this->m_open = true; + this->m_lock.unlock(); + this->connected(); + return status; + } + } + this->m_lock.unlock(); + return status; +} + +bool SocketComponentHelper::isOpened() { + bool is_open = false; + this->m_lock.lock(); + is_open = this->m_open; + this->m_lock.unlock(); + return is_open; +} + +SocketIpStatus SocketComponentHelper::reconnect() { + SocketIpStatus status = SOCK_SUCCESS; + + // Handle opening + + // Open a network connection if it has not already been open + if (this->isStarted() and (not this->isOpened())) { + status = this->open(); + } + return status; +} + +SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size) { + SocketIpStatus status = SOCK_SUCCESS; + this->m_lock.lock(); + NATIVE_INT_TYPE fd = this->m_fd; + this->m_lock.unlock(); + // Prevent transmission before connection, or after a disconnect + if (fd == -1) { + status = this->reconnect(); + // if reconnect wasn't successful, pass the that up to the caller + if(status != SOCK_SUCCESS) { + return status; + } + this->m_lock.lock(); + fd = this->m_fd; + this->m_lock.unlock(); + } + status = this->getSocketHandler().send(fd, data, size); + if (status == SOCK_DISCONNECTED) { + this->close(); + } + return status; +} + +void SocketComponentHelper::shutdown() { + this->m_lock.lock(); + this->getSocketHandler().shutdown(this->m_fd); + this->m_started = false; + this->m_fd = -1; + this->m_lock.unLock(); +} + +void SocketComponentHelper::close() { + this->m_lock.lock(); + if (this->m_fd != -1) { + this->getSocketHandler().close(this->m_fd); + this->m_fd = -1; + } + this->m_open = false; + this->m_lock.unlock(); +} + +Os::Task::Status SocketComponentHelper::join() { + return m_task.join(); +} + +void SocketComponentHelper::stop() { + this->m_lock.lock(); + this->m_stop = true; + this->m_lock.unlock(); + this->shutdown(); // Break out of any receives and fully shutdown +} + +SocketIpStatus SocketComponentHelper::recv(U8* data, U32 &size) { + SocketIpStatus status = SOCK_SUCCESS; + // Check for previously disconnected socket + this->m_lock.lock(); + NATIVE_INT_TYPE fd = this->m_fd; + this->m_lock.unlock(); + if (fd == -1) { + return SOCK_DISCONNECTED; + } + status = this->getSocketHandler().recv(fd, data, size); + if (status == SOCK_DISCONNECTED) { + this->close(); + } + return status; +} + +void SocketComponentHelper::readTask(void* pointer) { + FW_ASSERT(pointer); + SocketIpStatus status = SOCK_SUCCESS; + SocketComponentHelper* self = reinterpret_cast(pointer); + do { + // Prevent transmission before connection, or after a disconnect + if ((not self->isOpened()) and (not self->m_stop)) { + status = self->reconnect(); + if(status != SOCK_SUCCESS) { + Fw::Logger::log( + "[WARNING] Failed to open port with status %lu and errno %lu\n", + static_cast(status), + static_cast(errno)); + (void) Os::Task::delay(SOCKET_RETRY_INTERVAL); + continue; + } + } + // If the network connection is open, read from it + if (self->isStarted() and self->isOpened() and (not self->m_stop)) { + Fw::Buffer buffer = self->getBuffer(); + U8* data = buffer.getData(); + FW_ASSERT(data); + U32 size = buffer.getSize(); + // recv blocks, so it may have been a while since its done an isOpened check + status = self->recv(data, size); + if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN)) { + Fw::Logger::log("[WARNING] Failed to recv from port with status %lu and errno %lu\n", + static_cast(status), + static_cast(errno)); + self->close(); + buffer.setSize(0); + } else { + // Send out received data + buffer.setSize(size); + } + self->sendBuffer(buffer, status); + } + } + // As long as not told to stop, and we are successful interrupted or ordered to retry, keep receiving + while (not self->m_stop && + (status == SOCK_SUCCESS || status == SOCK_INTERRUPTED_TRY_AGAIN || self->m_reconnect)); + self->shutdown(); // Shutdown the port entirely +} +} // namespace Drv diff --git a/Drv/Ip/SocketReadTask.hpp b/Drv/Ip/SocketComponentHelper.hpp similarity index 73% rename from Drv/Ip/SocketReadTask.hpp rename to Drv/Ip/SocketComponentHelper.hpp index 932f93608d..e2604705d2 100644 --- a/Drv/Ip/SocketReadTask.hpp +++ b/Drv/Ip/SocketComponentHelper.hpp @@ -1,7 +1,7 @@ // ====================================================================== -// \title SocketReadTask.hpp +// \title SocketComponentHelper.hpp // \author mstarch -// \brief hpp file for SocketReadTask implementation class +// \brief hpp file for SocketComponentHelper implementation class // // \copyright // Copyright 2009-2020, by the California Institute of Technology. @@ -9,12 +9,13 @@ // acknowledged. // // ====================================================================== -#ifndef DRV_SOCKETREADTASK_HPP -#define DRV_SOCKETREADTASK_HPP +#ifndef DRV_SocketComponentHelper_HPP +#define DRV_SocketComponentHelper_HPP #include #include #include +#include namespace Drv { /** @@ -24,17 +25,17 @@ namespace Drv { * reading the data from the socket, sending the data out, and reopening the connection should a non-retry error occur. * */ -class SocketReadTask { +class SocketComponentHelper { public: /** * \brief constructs the socket read task */ - SocketReadTask(); + SocketComponentHelper(); /** * \brief destructor of the socket read task */ - virtual ~SocketReadTask(); + virtual ~SocketComponentHelper(); /** * \brief start the socket read task to start producing data @@ -66,6 +67,14 @@ class SocketReadTask { */ SocketIpStatus startup(); + /** + * \brief Returns true when the socket is started + * + * Returns true when the socket is started up sufficiently to be actively listening to clients. Returns false + * otherwise. This means `startup()` was called and returned success. + */ + bool isStarted(); + /** * \brief open the socket for communications * @@ -78,6 +87,45 @@ class SocketReadTask { */ SocketIpStatus open(); + /** + * \brief check if IP socket has previously been opened + * + * Check if this IpSocket has previously been opened. In the case of Udp this will check for outgoing transmissions + * and (if configured) incoming transmissions as well. This does not guarantee errors will not occur when using this + * socket as the remote component may have disconnected. + * + * \return true if socket is open, false otherwise + */ + bool isOpened(); + + /** + * \brief Re-open port if it has been disconnected + * + * + * \return status of reconnect, SOCK_SUCCESS for success, something else on error + */ + SocketIpStatus reconnect(); + + /** + * \brief send data to the IP socket from the given buffer + * + * + * \param data: pointer to data to send + * \param size: size of data to send + * \return status of send, SOCK_SUCCESS for success, something else on error + */ + SocketIpStatus send(const U8* const data, const U32 size); + + /** + * \brief receive data from the IP socket from the given buffer + * + * + * \param data: pointer to data to fill with received data + * \param size: maximum size of data buffer to fill + * \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error + */ + SocketIpStatus recv(U8* data, U32 &size); + /** * \brief close the socket communications * @@ -169,9 +217,14 @@ class SocketReadTask { static void readTask(void* pointer); Os::Task m_task; + Os::Mutex m_lock; + NATIVE_INT_TYPE m_fd; bool m_reconnect; //!< Force reconnection bool m_stop; //!< Stops the task when set to true - Os::Mutex m_task_lock; + bool m_started; //!< Have we successfully started the socket + bool m_open; //!< Have we successfully opened + + }; } -#endif // DRV_SOCKETREADTASK_HPP +#endif // DRV_SocketComponentHelper_HPP diff --git a/Drv/Ip/SocketReadTask.cpp b/Drv/Ip/SocketReadTask.cpp deleted file mode 100644 index 5a0a9eaf54..0000000000 --- a/Drv/Ip/SocketReadTask.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// ====================================================================== -// \title SocketReadTask.cpp -// \author mstarch -// \brief cpp file for SocketReadTask implementation class -// -// \copyright -// Copyright 2009-2020, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// -// ====================================================================== - -#include -#include -#include -#include - -#define MAXIMUM_SIZE 0x7FFFFFFF - -namespace Drv { - -SocketReadTask::SocketReadTask() : m_reconnect(false), m_stop(false) {} - -SocketReadTask::~SocketReadTask() {} - -void SocketReadTask::start(const Fw::StringBase &name, - const bool reconnect, - const Os::Task::ParamType priority, - const Os::Task::ParamType stack, - const Os::Task::ParamType cpuAffinity) { - FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times - FW_ASSERT(not this->m_stop); // It is a coding error to stop the thread before it is started - m_reconnect = reconnect; - // Note: the first step is for the IP socket to open the port - Os::Task::Arguments arguments(name, SocketReadTask::readTask, this, priority, stack, cpuAffinity); - Os::Task::Status stat = m_task.start(arguments); - FW_ASSERT(Os::Task::OP_OK == stat, static_cast(stat)); -} - -SocketIpStatus SocketReadTask::startup() { - return this->getSocketHandler().startup(); -} - -SocketIpStatus SocketReadTask::open() { - SocketIpStatus status = this->getSocketHandler().open(); - // Call connected any time the open is successful - if (Drv::SOCK_SUCCESS == status) { - this->connected(); - } - return status; -} - -void SocketReadTask::shutdown() { - this->m_task_lock.lock(); - this->getSocketHandler().shutdown(); - this->m_task_lock.unlock(); -} - -void SocketReadTask::close() { - this->m_task_lock.lock(); - this->getSocketHandler().close(); - this->m_task_lock.unlock(); -} - -Os::Task::Status SocketReadTask::join() { - return m_task.join(); -} - -void SocketReadTask::stop() { - this->m_task_lock.lock(); - this->m_stop = true; - this->getSocketHandler().shutdown(); // Break out of any receives and fully shutdown - this->m_task_lock.unlock(); -} - -void SocketReadTask::readTask(void* pointer) { - FW_ASSERT(pointer); - SocketIpStatus status = SOCK_SUCCESS; - SocketReadTask* self = reinterpret_cast(pointer); - do { - self->m_task_lock.lock(); - // Open a network connection if it has not already been open - if ((not self->getSocketHandler().isStarted()) and (not self->m_stop) and - ((status = self->startup()) != SOCK_SUCCESS)) { - Fw::Logger::log( - "[WARNING] Failed to open port with status %d and errno %d\n", - static_cast(status), - static_cast(errno)); - self->m_task_lock.unlock(); - (void) Os::Task::delay(SOCKET_RETRY_INTERVAL); - continue; - } - - // Open a network connection if it has not already been open - if ((not self->getSocketHandler().isOpened()) and (not self->m_stop) and - ((status = self->open()) != SOCK_SUCCESS)) { - Fw::Logger::log( - "[WARNING] Failed to open port with status %d and errno %d\n", - static_cast(status), - static_cast(errno)); - self->m_task_lock.unlock(); - (void) Os::Task::delay(SOCKET_RETRY_INTERVAL); - continue; - } - - // If the network connection is open, read from it - if (self->getSocketHandler().isStarted() and self->getSocketHandler().isOpened() and (not self->m_stop)) { - Fw::Buffer buffer = self->getBuffer(); - U8* data = buffer.getData(); - FW_ASSERT(data); - U32 size = buffer.getSize(); - status = self->getSocketHandler().recv(data, size); - if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN)) { - Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n", - static_cast(status), - static_cast(errno)); - self->getSocketHandler().close(); - buffer.setSize(0); - } else { - // Send out received data - buffer.setSize(size); - } - self->sendBuffer(buffer, status); - } - self->m_task_lock.unlock(); - } - // As long as not told to stop, and we are successful interrupted or ordered to retry, keep receiving - while (not self->m_stop && - (status == SOCK_SUCCESS || status == SOCK_INTERRUPTED_TRY_AGAIN || self->m_reconnect)); - self->getSocketHandler().shutdown(); // Shutdown the port entirely -} -} // namespace Drv diff --git a/Drv/Ip/TcpClientSocket.cpp b/Drv/Ip/TcpClientSocket.cpp index 9c93184e44..8ad574badd 100644 --- a/Drv/Ip/TcpClientSocket.cpp +++ b/Drv/Ip/TcpClientSocket.cpp @@ -57,9 +57,7 @@ SocketIpStatus TcpClientSocket::openProtocol(NATIVE_INT_TYPE& fd) { } // Set up the address port and name address.sin_family = AF_INET; - this->m_lock.lock(); address.sin_port = htons(this->m_port); - this->m_lock.unlock(); // OS specific settings #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN @@ -88,12 +86,12 @@ SocketIpStatus TcpClientSocket::openProtocol(NATIVE_INT_TYPE& fd) { return SOCK_SUCCESS; } -I32 TcpClientSocket::sendProtocol(const U8* const data, const U32 size) { - return static_cast(::send(this->m_fd, data, size, SOCKET_IP_SEND_FLAGS)); +I32 TcpClientSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) { + return static_cast(::send(fd, data, size, SOCKET_IP_SEND_FLAGS)); } -I32 TcpClientSocket::recvProtocol(U8* const data, const U32 size) { - return static_cast(::recv(this->m_fd, data, size, SOCKET_IP_RECV_FLAGS)); +I32 TcpClientSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) { + return static_cast(::recv(fd, data, size, SOCKET_IP_RECV_FLAGS)); } } // namespace Drv diff --git a/Drv/Ip/TcpClientSocket.hpp b/Drv/Ip/TcpClientSocket.hpp index a1a7ae3232..cd12f58cc6 100644 --- a/Drv/Ip/TcpClientSocket.hpp +++ b/Drv/Ip/TcpClientSocket.hpp @@ -50,18 +50,20 @@ class TcpClientSocket : public IpSocket { SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override; /** * \brief Protocol specific implementation of send. Called directly with retry from send. + * \param fd: file descriptor to send to * \param data: data to send * \param size: size of data to send * \return: size of data sent, or -1 on error. */ - I32 sendProtocol(const U8* const data, const U32 size) override; + I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override; /** * \brief Protocol specific implementation of recv. Called directly with error handling from recv. + * \param fd: file descriptor to recv from * \param data: data pointer to fill * \param size: size of data buffer * \return: size of data received, or -1 on error. */ - I32 recvProtocol( U8* const data, const U32 size) override; + I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override; }; } // namespace Drv diff --git a/Drv/Ip/TcpServerSocket.cpp b/Drv/Ip/TcpServerSocket.cpp index a26727dd2f..5f29658959 100644 --- a/Drv/Ip/TcpServerSocket.cpp +++ b/Drv/Ip/TcpServerSocket.cpp @@ -41,25 +41,20 @@ namespace Drv { TcpServerSocket::TcpServerSocket() : IpSocket(), m_base_fd(-1) {} U16 TcpServerSocket::getListenPort() { - this->m_lock.lock(); U16 port = this->m_port; - this->m_lock.unlock(); return port; } SocketIpStatus TcpServerSocket::startup() { NATIVE_INT_TYPE serverFd = -1; struct sockaddr_in address; - this->close(); // Acquire a socket, or return error if ((serverFd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) { return SOCK_FAILED_TO_GET_SOCKET; } // Set up the address port and name address.sin_family = AF_INET; - this->m_lock.lock(); address.sin_port = htons(this->m_port); - this->m_lock.unlock(); // OS specific settings #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN @@ -90,37 +85,26 @@ SocketIpStatus TcpServerSocket::startup() { return SOCK_FAILED_TO_LISTEN; // What we have here is a failure to communicate } - this->m_lock.lock(); m_base_fd = serverFd; m_port = port; - this->m_lock.unLock(); return this->IpSocket::startup(); } -void TcpServerSocket::shutdown() { - this->m_lock.lock(); +void TcpServerSocket::shutdown(NATIVE_INT_TYPE fd) { if (this->m_base_fd != -1) { (void)::shutdown(this->m_base_fd, SHUT_RDWR); (void)::close(this->m_base_fd); this->m_base_fd = -1; } - this->m_lock.unLock(); - this->IpSocket::shutdown(); + this->IpSocket::shutdown(fd); } SocketIpStatus TcpServerSocket::openProtocol(NATIVE_INT_TYPE& fd) { NATIVE_INT_TYPE clientFd = -1; NATIVE_INT_TYPE serverFd = -1; - // Check started before allowing open - if (not this->isStarted()) { - return SOCK_NOT_STARTED; - } - - this->m_lock.lock(); serverFd = this->m_base_fd; - this->m_lock.unLock(); // TCP requires accepting on the socket to get the client socket file descriptor. clientFd = ::accept(serverFd, nullptr, nullptr); @@ -138,12 +122,15 @@ SocketIpStatus TcpServerSocket::openProtocol(NATIVE_INT_TYPE& fd) { return SOCK_SUCCESS; } -I32 TcpServerSocket::sendProtocol(const U8* const data, const U32 size) { - return static_cast(::send(this->m_fd, data, size, SOCKET_IP_SEND_FLAGS)); +I32 TcpServerSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) { + return static_cast(::send(fd, data, size, SOCKET_IP_SEND_FLAGS)); } -I32 TcpServerSocket::recvProtocol(U8* const data, const U32 size) { - return static_cast(::recv(this->m_fd, data, size, SOCKET_IP_RECV_FLAGS)); +I32 TcpServerSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) { + I32 size_buf; + // recv will return 0 if the client has done an orderly shutdown + size_buf = static_cast(::recv(fd, data, size, SOCKET_IP_RECV_FLAGS)); + return size_buf; } } // namespace Drv diff --git a/Drv/Ip/TcpServerSocket.hpp b/Drv/Ip/TcpServerSocket.hpp index 335a634e96..6e639d15c1 100644 --- a/Drv/Ip/TcpServerSocket.hpp +++ b/Drv/Ip/TcpServerSocket.hpp @@ -43,10 +43,11 @@ class TcpServerSocket : public IpSocket { /** * \brief Shutdown and close the server socket followed by the open client * + * \param fd: file descriptor to shutdown * First, this calls `shutdown` and `close` on the server socket and then calls the close method to `shutdown` and * `close` the client. */ - void shutdown() override; + void shutdown(NATIVE_INT_TYPE fd) override; /** * \brief get the port being listened on @@ -67,19 +68,21 @@ class TcpServerSocket : public IpSocket { SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override; /** * \brief Protocol specific implementation of send. Called directly with retry from send. + * \param fd: file descriptor to send to * \param data: data to send * \param size: size of data to send * \return: size of data sent, or -1 on error. */ - I32 sendProtocol(const U8* const data, const U32 size) override; + I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override; /** * \brief Protocol specific implementation of recv. Called directly with error handling from recv. + * \param fd: file descriptor to recv from * \param data: data pointer to fill * \param size: size of data buffer * \return: size of data received, or -1 on error. */ - I32 recvProtocol( U8* const data, const U32 size) override; - private: + I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override; + PRIVATE: NATIVE_INT_TYPE m_base_fd; //!< File descriptor of the listening socket }; } // namespace Drv diff --git a/Drv/Ip/UdpSocket.cpp b/Drv/Ip/UdpSocket.cpp index 52902454cd..59ca76e5bf 100644 --- a/Drv/Ip/UdpSocket.cpp +++ b/Drv/Ip/UdpSocket.cpp @@ -69,17 +69,13 @@ SocketIpStatus UdpSocket::configureSend(const char* const hostname, const U16 po SocketIpStatus UdpSocket::configureRecv(const char* hostname, const U16 port) { FW_ASSERT(this->isValidPort(port)); FW_ASSERT(hostname != nullptr); - this->m_lock.lock(); this->m_recv_port = port; (void) Fw::StringUtils::string_copy(this->m_recv_hostname, hostname, static_cast(SOCKET_MAX_HOSTNAME_SIZE)); - this->m_lock.unlock(); return SOCK_SUCCESS; } U16 UdpSocket::getRecvPort() { - this->m_lock.lock(); U16 port = this->m_recv_port; - this->m_lock.unlock(); return port; } @@ -90,9 +86,7 @@ SocketIpStatus UdpSocket::bind(NATIVE_INT_TYPE fd) { // Set up the address port and name address.sin_family = AF_INET; - this->m_lock.lock(); - address.sin_port = htons(m_recv_port); - this->m_lock.unlock(); + address.sin_port = htons(this->m_recv_port); // OS specific settings #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN address.sin_len = static_cast(sizeof(struct sockaddr_in)); @@ -111,13 +105,9 @@ SocketIpStatus UdpSocket::bind(NATIVE_INT_TYPE fd) { if (::getsockname(fd, reinterpret_cast(&address), &size) == -1) { return SOCK_FAILED_TO_READ_BACK_PORT; } - U16 port = ntohs(address.sin_port); - this->m_lock.lock(); FW_ASSERT(sizeof(this->m_state->m_addr_recv) == sizeof(address), sizeof(this->m_state->m_addr_recv), sizeof(address)); memcpy(&this->m_state->m_addr_recv, &address, sizeof(this->m_state->m_addr_recv)); - this->m_recv_port = port; - this->m_lock.unlock(); return SOCK_SUCCESS; } @@ -127,9 +117,7 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) { NATIVE_INT_TYPE socketFd = -1; struct sockaddr_in address; - this->m_lock.lock(); U16 port = this->m_port; - this->m_lock.unlock(); // Acquire a socket, or return error if ((socketFd = ::socket(AF_INET, SOCK_DGRAM, 0)) == -1) { @@ -140,9 +128,7 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) { if (port != 0) { // Set up the address port and name address.sin_family = AF_INET; - this->m_lock.lock(); address.sin_port = htons(this->m_port); - this->m_lock.unlock(); // OS specific settings #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN @@ -160,11 +146,9 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) { ::close(socketFd); return status; } - this->m_lock.lock(); FW_ASSERT(sizeof(this->m_state->m_addr_send) == sizeof(address), sizeof(this->m_state->m_addr_send), sizeof(address)); memcpy(&this->m_state->m_addr_send, &address, sizeof(this->m_state->m_addr_send)); - this->m_lock.unlock(); } // When we are setting up for receiving as well, then we must bind to a port @@ -172,9 +156,7 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) { ::close(socketFd); return status; // Not closing FD as it is still a valid send FD } - this->m_lock.lock(); U16 recv_port = this->m_recv_port; - this->m_lock.unlock(); // Log message for UDP if (port == 0) { Fw::Logger::log("Setup to receive udp at %s:%hu\n", m_recv_hostname, @@ -191,15 +173,15 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) { return status; } -I32 UdpSocket::sendProtocol(const U8* const data, const U32 size) { +I32 UdpSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) { FW_ASSERT(this->m_state->m_addr_send.sin_family != 0); // Make sure the address was previously setup - return static_cast(::sendto(this->m_fd, data, size, SOCKET_IP_SEND_FLAGS, + return static_cast(::sendto(fd, data, size, SOCKET_IP_SEND_FLAGS, reinterpret_cast(&this->m_state->m_addr_send), sizeof(this->m_state->m_addr_send))); } -I32 UdpSocket::recvProtocol(U8* const data, const U32 size) { +I32 UdpSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) { FW_ASSERT(this->m_state->m_addr_recv.sin_family != 0); // Make sure the address was previously setup - return static_cast(::recvfrom(this->m_fd, data, size, SOCKET_IP_RECV_FLAGS, nullptr, nullptr)); + return static_cast(::recvfrom(fd, data, size, SOCKET_IP_RECV_FLAGS, nullptr, nullptr)); } } // namespace Drv diff --git a/Drv/Ip/UdpSocket.hpp b/Drv/Ip/UdpSocket.hpp index 82d1ebf812..06d21e7057 100644 --- a/Drv/Ip/UdpSocket.hpp +++ b/Drv/Ip/UdpSocket.hpp @@ -101,18 +101,20 @@ class UdpSocket : public IpSocket { SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override; /** * \brief Protocol specific implementation of send. Called directly with retry from send. + * \param fd: file descriptor to send to * \param data: data to send * \param size: size of data to send * \return: size of data sent, or -1 on error. */ - I32 sendProtocol(const U8* const data, const U32 size) override; + I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override; /** * \brief Protocol specific implementation of recv. Called directly with error handling from recv. + * \param fd: file descriptor to recv from * \param data: data pointer to fill * \param size: size of data buffer * \return: size of data received, or -1 on error. */ - I32 recvProtocol( U8* const data, const U32 size) override; + I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override; private: SocketState* m_state; //!< State storage U16 m_recv_port; //!< IP address port used diff --git a/Drv/Ip/docs/sdd.md b/Drv/Ip/docs/sdd.md index 8e2ab23940..ca2be62588 100644 --- a/Drv/Ip/docs/sdd.md +++ b/Drv/Ip/docs/sdd.md @@ -11,8 +11,8 @@ interacting with sockets generically. Drv::IpSocket delegates protocol specific implemented by the children concrete socket classes. i.e. Drv::IpSocket::open delegates to the functions Drv::TcpClientSocket::openProtocol to open up specifically a tcp client socket. -Drv::SocketReadTask is a virtual base class that comes with the functionality for setting up a generic reading thread -complete with the ability to reconnect to a closed/broken connection. This virtual base class is intended to be +Drv::SocketComponentHelper is a virtual base class that comes with the functionality for setting up a generic reading thread +complete with the ability to reconnect to a closed/broken connection. It exists at the component level and serves as a passthrough for requests from the F` component to the IPv4 sockets. This virtual base class is intended to be inherited by an F´ component wrapper that need to support a receive thread such that this functionality need not be redundantly implemented. @@ -22,7 +22,7 @@ Each of these classes is explained in more detail below. - [Drv::TcpClientSocket](#drvtcpclientsocket-class) - [Drv::TcpServerSocket](#drvtcpserversocket-class) - [Drv::UdpSocket](#drvudpsocket-class) -- [Drv::SocketReadTask](#drvsocketreadtask-virtual-baseclass) +- [Drv::SocketComponentHelper](#drvsocketreadtask-virtual-baseclass) ## Drv::IpSocket Baseclass @@ -40,13 +40,13 @@ system resources and form a connection. In server implementations (Drv::TcpServ a client connection has been made. It is safe to assume that a successfully opened socket is ready to send or receive, however; those calls may detect an error and close the socket in response. -`Drv::TcpServerSocket::send` will attempt to send data across the socket. It will retry to transmit data a configured +`Drv::IpSocket::send` will attempt to send data across the socket. It will retry to transmit data a configured number of times on correctable errors before finally succeeding once all data has been transmitted or failing should the socket send fail. Interrupts and timeouts are the only recoverable errors. Other problems result in an error status and when a remote disconnect is detected `Drv::IpSocket::close` is closed to ensure the socket is ready for a subsequent call to `Drv::IpSocket::open`. -`Drv::TcpServerSocket::recv` will attempt to read data from across the socket. It will block until data is received and +`Drv::IpSocket::recv` will attempt to read data from across the socket. It will block until data is received and in the case that the socket is interrupted without data, it will retry a configurable number of times. Other errors will result in an error status with a specific `Drv::IpSocket::close` call issued in the case of detected disconnects. @@ -130,7 +130,7 @@ The Drv::UdpSocket class represents an IPv4 UDP sender/receiver. Drv::UdpSocket bidirectional communication using UDP. UDP is typically faster than TCP as it does not acknowledge transmissions. There is no guarantee that a sent packet is received, or even that the remote side is listening. -A udp socket must be configured for each direction that it will communicate in. This can be done using calls to +A UDP socket must be configured for each direction that it will communicate in. This can be done using calls to `Drv::UdpSocket::configureSend` and `Drv::UdpSocket::configureRecv`. If either call is omitted only a single direction of communication will function. It is erroneous to omit both configuration calls. Calling `Drv::UdpSocket::configure` is equivalent to calling `Drv::UdpSocket::configureSend` for compatibility with `Drv::IpSocket`. Other interaction @@ -152,43 +152,42 @@ socketBoth.configureRecv(127.0.0.1, 60212); ... ``` -## Drv::SocketReadTask Virtual Baseclass +## Drv::SocketComponentHelper Virtual Baseclass -The Drv::SocketReadTask is intended as a base class used to add in the functionality of an automatically reconnecting -receive thread to another class (typically an F´ component). In order for this thread to function, the inheritor must +The Drv::SocketComponentHelper is intended as a base class used to add in the functionality of an automatically reconnecting +receive thread to another class (typically an F´ component) as well as an interface between the component using an IP socket and the IP socket library functions implemented in this folder. In order for this thread to function, the inheritor must implement several methods to provide the necessary interaction of this thread. These functions are described in the next section. -In order to start the receiving thread a call to the `Drv::SocketReadTask::startSocketTask` method is performed passing +In order to start the receiving thread a call to the `Drv::SocketComponentHelper::start` method is performed passing in a name, and all arguments to `Os::Task::start` to start the task. An optional parameter, reconnect, will determine if this read task will reconnect to sockets should a disconnect or error occur. Once started the read task will continue -until a `Drv::SocketReadTask::stopSocketTask` has been called or an error occurred when started without reconnect set to -`true`. Once the socket stop call has been made, the user should call `Drv::SocketReadTask::joinSocketTask` in order to -wait until the full task has finished. `Drv::SocketReadTask::stopSocketTask` will call `Drv::IpSocket::close` on the +until a `Drv::SocketComponentHelper::stop` has been called or an error occurred when started without reconnect set to +`true`. Once the socket stop call has been made, the user should call `Drv::SocketComponentHelper::join` in order to +wait until the full task has finished. `Drv::SocketComponentHelper::stop` will call `Drv::SocketComponentHelper::close` on the provided Drv::IpSocket to ensure that any blocking reads exit freeing the thread to completely stop. Normal usage of -a Drv::SocketReadTask derived class is shown below. +a Drv::SocketComponentHelper derived class is shown below. ```c++ Os::TaskString name("ReceiveTask"); -uplinkComm.startSocketTask(name); // Default reconnect=true +uplinkComm.start(name); // Default reconnect=true ... -uplinkComm.stopSocketTask(); -(void) uplinkComm.joinSocketTask(nullptr); +uplinkComm.stop(); +(void) uplinkComm.join(nullptr); ``` -`Drv::SocketReadTask::open` and `Drv::SocketReadTask::close` convenience methods are also provided to open and close the -provided Drv::IpSocket, although it should be noted that both are called automatically during the normal process of the -receive thread. +`Drv::SocketComponentHelper::open` and `Drv::SocketComponentHelper::close` convenience methods are also provided to open and close the +provided Drv::IpSocket, although it should be noted that both are called automatically either by the receive thread or on send. -### Drv::SocketReadTask Inheritance +### Drv::SocketComponentHelper Inheritance -Drv::SocketReadTask is used via inheritance. A class that needs to provide receive thread functionality may inherit from +Drv::SocketComponentHelper is used via inheritance. A class that needs to provide receive thread functionality may inherit from this class and implement the three virtual methods to integrate with the thread. These methods are described below and each provides its prototype. -`Drv::SocketReadTask::getSocketHandler` returns a reference to an existing Drv::IpSocket for this task to interact with +`Drv::SocketComponentHelper::getSocketHandler` returns a reference to an existing Drv::IpSocket for this task to interact with for receiving, sending, opening, and closing. This should not be allocated within the function call as it will be used outside the call. The instance is expected to have been configured and started (in the case of servers). The prototype of the function is shown below. @@ -197,15 +196,15 @@ of the function is shown below. virtual IpSocket& getSocketHandler() = 0; ``` -`Drv::SocketReadTask::getBuffer` returns an Fw::Buffer instance that wraps data for the read call to fill. This can wrap +`Drv::SocketComponentHelper::getBuffer` returns an Fw::Buffer instance that wraps data for the read call to fill. This can wrap stack memory, or delegate the call to a buffer manager instance. This buffer will be returned via a call to -`Drv::SocketReadTask::sendBuffer` once the received data has filled it. The prototype of the function is shown below. +`Drv::SocketComponentHelper::sendBuffer` once the received data has filled it. The prototype of the function is shown below. ```c++ virtual Fw::Buffer getBuffer() = 0; ``` -`Drv::SocketReadTask::sendBuffer` returns the buffer obtained from `Drv::SocketReadTask::getBuffer` after it has been +`Drv::SocketComponentHelper::sendBuffer` returns the buffer obtained from `Drv::SocketComponentHelper::getBuffer` after it has been filled with data read from the socket. This will come with a status associated with the read but will always be called such that the allocated buffer can be freed when needed. diff --git a/Drv/Ip/test/ut/SocketTestHelper.cpp b/Drv/Ip/test/ut/SocketTestHelper.cpp index a52d2e3b7e..dbf7c6c346 100644 --- a/Drv/Ip/test/ut/SocketTestHelper.cpp +++ b/Drv/Ip/test/ut/SocketTestHelper.cpp @@ -17,12 +17,12 @@ namespace Test { const U32 MAX_DRV_TEST_MESSAGE_SIZE = 1024; -void force_recv_timeout(Drv::IpSocket& socket) { +void force_recv_timeout(NATIVE_INT_TYPE fd, Drv::IpSocket& socket) { // Set timeout socket option struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 50; // 50ms max before test failure - setsockopt(socket.m_fd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(timeout)); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(timeout)); } void validate_random_data(U8 *data, U8 *truth, U32 size) { @@ -48,39 +48,19 @@ void fill_random_buffer(Fw::Buffer &buffer) { fill_random_data(buffer.getData(), buffer.getSize()); } -void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver) { +void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, NATIVE_INT_TYPE sender_fd, NATIVE_INT_TYPE receiver_fd) { U32 size = MAX_DRV_TEST_MESSAGE_SIZE; U8 buffer_out[MAX_DRV_TEST_MESSAGE_SIZE] = {0}; U8 buffer_in[MAX_DRV_TEST_MESSAGE_SIZE] = {0}; // Send receive validate block Drv::Test::fill_random_data(buffer_out, MAX_DRV_TEST_MESSAGE_SIZE); - EXPECT_EQ(sender.send(buffer_out, MAX_DRV_TEST_MESSAGE_SIZE), Drv::SOCK_SUCCESS); - EXPECT_EQ(receiver.recv(buffer_in, size), Drv::SOCK_SUCCESS); + EXPECT_EQ(sender.send(sender_fd, buffer_out, MAX_DRV_TEST_MESSAGE_SIZE), Drv::SOCK_SUCCESS); + EXPECT_EQ(receiver.recv(receiver_fd, buffer_in, size), Drv::SOCK_SUCCESS); EXPECT_EQ(size, static_cast(MAX_DRV_TEST_MESSAGE_SIZE)); Drv::Test::validate_random_data(buffer_out, buffer_in, MAX_DRV_TEST_MESSAGE_SIZE); } -bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations) { - for (U32 i = 0; i < iterations; i++) { - if (open == socket.isOpened()) { - return true; - } - Os::Task::delay(Fw::Time(0, 10000)); - } - return false; -} - -bool wait_on_started(Drv::IpSocket &socket, bool open, U32 iterations) { - for (U32 i = 0; i < iterations; i++) { - if (open == socket.isStarted()) { - return true; - } - Os::Task::delay(Fw::Time(0, 10000)); - } - return false; -} - U64 get_configured_delay_ms() { return (static_cast(SOCKET_RETRY_INTERVAL.getSeconds()) * 1000) + (static_cast(SOCKET_RETRY_INTERVAL.getUSeconds()) / 1000); diff --git a/Drv/Ip/test/ut/SocketTestHelper.hpp b/Drv/Ip/test/ut/SocketTestHelper.hpp index ad6e3a2de7..ca58b1b697 100644 --- a/Drv/Ip/test/ut/SocketTestHelper.hpp +++ b/Drv/Ip/test/ut/SocketTestHelper.hpp @@ -16,9 +16,10 @@ namespace Test { /** * Force a receive timeout on a socket such that it will not hang our testing despite the normal recv behavior of * "block forever" until it gets data. + * @param fd: socket file descriptor * @param socket: socket to make timeout */ -void force_recv_timeout(Drv::IpSocket &socket); +void force_recv_timeout(NATIVE_INT_TYPE fd, Drv::IpSocket &socket); /** * Validate random data from data against truth @@ -52,8 +53,10 @@ void fill_random_buffer(Fw::Buffer &buffer); * Send/receive pair. * @param sender: sender of the pair * @param receiver: receiver of pair + * @param sender_fd: file descriptor for sender + * @param receiver_fd: file descriptor for receiver */ -void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver); +void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, NATIVE_INT_TYPE sender_fd, NATIVE_INT_TYPE receiver_fd); /** * Wait on socket change. diff --git a/Drv/Ip/test/ut/TestTcp.cpp b/Drv/Ip/test/ut/TestTcp.cpp index d06f0dd5d4..64ad86dcbe 100644 --- a/Drv/Ip/test/ut/TestTcp.cpp +++ b/Drv/Ip/test/ut/TestTcp.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -18,33 +19,37 @@ void test_with_loop(U32 iterations) { U16 port = 0; // Choose a port Drv::TcpServerSocket server; + NATIVE_INT_TYPE server_fd = -1; + NATIVE_INT_TYPE client_fd = -1; server.configure("127.0.0.1", port, 0, 100); EXPECT_EQ(server.startup(), Drv::SOCK_SUCCESS); - Drv::Test::force_recv_timeout(server); + Drv::Test::force_recv_timeout(server_fd, server); // Loop through a bunch of client disconnects for (U32 i = 0; i < iterations; i++) { Drv::TcpClientSocket client; client.configure("127.0.0.1", server.getListenPort(),0,100); - status1 = client.open(); + // client_fd gets assigned a real value here + status1 = client.open(client_fd); EXPECT_EQ(status1, Drv::SOCK_SUCCESS); - status2 = server.open(); + // client_fd gets assigned a real value here + status2 = server.open(server_fd); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); // If all the opens worked, then run this if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) { // Force the sockets not to hang, if at all possible - Drv::Test::force_recv_timeout(client); - Drv::Test::force_recv_timeout(server); - Drv::Test::send_recv(server, client); - Drv::Test::send_recv(client, server); + Drv::Test::force_recv_timeout(client_fd, client); + Drv::Test::force_recv_timeout(server_fd, server); + Drv::Test::send_recv(server, client, server_fd, client_fd); + Drv::Test::send_recv(client, server, client_fd, server_fd); } - client.close(); - server.close(); + client.close(client_fd); + server.close(server_fd); } - server.shutdown(); + server.shutdown(server_fd); } diff --git a/Drv/Ip/test/ut/TestUdp.cpp b/Drv/Ip/test/ut/TestUdp.cpp index 76fe4591d1..95b97d1de0 100644 --- a/Drv/Ip/test/ut/TestUdp.cpp +++ b/Drv/Ip/test/ut/TestUdp.cpp @@ -15,8 +15,11 @@ void test_with_loop(U32 iterations, bool duplex) { Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS; Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS; - // When not duplex, we can allow the OS to choose a port - U16 port1 = (duplex) ? Drv::Test::get_free_port(true) : 0; + NATIVE_INT_TYPE udp1_fd = -1; + NATIVE_INT_TYPE udp2_fd = -1; + + U16 port1 = Drv::Test::get_free_port(true); + ASSERT_NE(0, port1); U16 port2 = port1; for (U8 i = 0; (i < std::numeric_limits::max()) && (port2 == port1); i++) { port2 = Drv::Test::get_free_port(true); @@ -35,27 +38,27 @@ void test_with_loop(U32 iterations, bool duplex) { if (duplex) { udp2.configureSend("127.0.0.1", port2, 0, 100); } - status2 = udp2.open(); + status2 = udp2.open(udp2_fd); ASSERT_EQ(status2, Drv::SOCK_SUCCESS); udp1.configureSend("127.0.0.1", udp2.getRecvPort(), 0, 100); udp1.configureRecv("127.0.0.1", port2); - status1 = udp1.open(); + status1 = udp1.open(udp1_fd); ASSERT_EQ(status1, Drv::SOCK_SUCCESS); // If all the opens worked, then run this if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) { // Force the sockets not to hang, if at all possible - Drv::Test::force_recv_timeout(udp1); - Drv::Test::force_recv_timeout(udp2); - Drv::Test::send_recv(udp1, udp2); + Drv::Test::force_recv_timeout(udp1_fd, udp1); + Drv::Test::force_recv_timeout(udp2_fd, udp2); + Drv::Test::send_recv(udp1, udp2, udp1_fd, udp2_fd); // Allow duplex connections if (duplex) { - Drv::Test::send_recv(udp2, udp1); + Drv::Test::send_recv(udp2, udp1, udp2_fd, udp1_fd); } } - udp1.close(); - udp2.close(); + udp1.close(udp1_fd); + udp2.close(udp2_fd); } } diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp index 46c40318dc..97a4613326 100644 --- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp +++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.hpp @@ -34,12 +34,6 @@ namespace Drv { const char *const compName /*!< The component name*/ ); - //! Initialize object LinuxGpioDriver - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object LinuxGpioDriver //! ~LinuxGpioDriverComponentImpl(); diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplCommon.cpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplCommon.cpp index 3fc402d806..a46ff9fd3d 100644 --- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplCommon.cpp +++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImplCommon.cpp @@ -32,12 +32,4 @@ namespace Drv { } - void LinuxGpioDriverComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - LinuxGpioDriverComponentBase::init(instance); - } - } // end namespace Drv diff --git a/Drv/LinuxI2cDriver/LinuxI2cDriver.cpp b/Drv/LinuxI2cDriver/LinuxI2cDriver.cpp index 2f01887d8c..8618515164 100644 --- a/Drv/LinuxI2cDriver/LinuxI2cDriver.cpp +++ b/Drv/LinuxI2cDriver/LinuxI2cDriver.cpp @@ -39,14 +39,6 @@ namespace Drv { } - void LinuxI2cDriver :: - init( - const NATIVE_INT_TYPE instance - ) - { - LinuxI2cDriverComponentBase::init(instance); - } - LinuxI2cDriver:: ~LinuxI2cDriver() { diff --git a/Drv/LinuxI2cDriver/LinuxI2cDriver.hpp b/Drv/LinuxI2cDriver/LinuxI2cDriver.hpp index 02fdafd9c0..44fe86f234 100644 --- a/Drv/LinuxI2cDriver/LinuxI2cDriver.hpp +++ b/Drv/LinuxI2cDriver/LinuxI2cDriver.hpp @@ -31,12 +31,6 @@ namespace Drv { //! LinuxI2cDriver(const char *const compName); - //! Initialize object LinuxI2cDriver - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - bool open(const char* device); //! Destroy object LinuxI2cDriver //! diff --git a/Drv/LinuxI2cDriver/LinuxI2cDriverStub.cpp b/Drv/LinuxI2cDriver/LinuxI2cDriverStub.cpp index b3346aed89..da3b0f673a 100644 --- a/Drv/LinuxI2cDriver/LinuxI2cDriverStub.cpp +++ b/Drv/LinuxI2cDriver/LinuxI2cDriverStub.cpp @@ -29,14 +29,6 @@ LinuxI2cDriver ::LinuxI2cDriver( } - void LinuxI2cDriver :: - init( - const NATIVE_INT_TYPE instance - ) - { - LinuxI2cDriverComponentBase::init(instance); - } - LinuxI2cDriver :: ~LinuxI2cDriver() { diff --git a/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp b/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp index 51e2a3b163..1ffe97a3e8 100644 --- a/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp +++ b/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp @@ -66,11 +66,6 @@ namespace Drv { const char * const compName /*!< The component name*/ ); - //! Initialize object LinuxSpiDriver - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object LinuxSpiDriver //! ~LinuxSpiDriverComponentImpl(); diff --git a/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImplCommon.cpp b/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImplCommon.cpp index 7a8b03f451..852ad5c7f5 100644 --- a/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImplCommon.cpp +++ b/Drv/LinuxSpiDriver/LinuxSpiDriverComponentImplCommon.cpp @@ -30,9 +30,4 @@ namespace Drv { } - void LinuxSpiDriverComponentImpl::init(const NATIVE_INT_TYPE instance) { - LinuxSpiDriverComponentBase::init(instance); - } - - } // end namespace Drv diff --git a/Drv/LinuxUartDriver/LinuxUartDriver.cpp b/Drv/LinuxUartDriver/LinuxUartDriver.cpp index 0524644e4b..2fbdcb16a0 100644 --- a/Drv/LinuxUartDriver/LinuxUartDriver.cpp +++ b/Drv/LinuxUartDriver/LinuxUartDriver.cpp @@ -35,10 +35,6 @@ LinuxUartDriver ::LinuxUartDriver(const char* const compName) : LinuxUartDriverComponentBase(compName), m_fd(-1), m_allocationSize(0), m_device("NOT_EXIST"), m_quitReadThread(false) { } -void LinuxUartDriver ::init(const NATIVE_INT_TYPE instance) { - LinuxUartDriverComponentBase::init(instance); -} - bool LinuxUartDriver::open(const char* const device, UartBaudRate baud, UartFlowControl fc, diff --git a/Drv/LinuxUartDriver/LinuxUartDriver.hpp b/Drv/LinuxUartDriver/LinuxUartDriver.hpp index 0e6bb5adca..447d2cf423 100644 --- a/Drv/LinuxUartDriver/LinuxUartDriver.hpp +++ b/Drv/LinuxUartDriver/LinuxUartDriver.hpp @@ -32,11 +32,6 @@ class LinuxUartDriver : public LinuxUartDriverComponentBase { LinuxUartDriver(const char* const compName /*!< The component name*/ ); - //! Initialize object LinuxUartDriver - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Configure UART parameters enum UartBaudRate { BAUD_9600=9600, diff --git a/Drv/TcpClient/TcpClientComponentImpl.cpp b/Drv/TcpClient/TcpClientComponentImpl.cpp index 57eeda47a6..f89e49c60d 100644 --- a/Drv/TcpClient/TcpClientComponentImpl.cpp +++ b/Drv/TcpClient/TcpClientComponentImpl.cpp @@ -24,7 +24,7 @@ namespace Drv { TcpClientComponentImpl::TcpClientComponentImpl(const char* const compName) : TcpClientComponentBase(compName), - SocketReadTask() {} + SocketComponentHelper() {} SocketIpStatus TcpClientComponentImpl::configure(const char* hostname, const U16 port, @@ -35,6 +35,7 @@ SocketIpStatus TcpClientComponentImpl::configure(const char* hostname, // Check that ensures the configured buffer size fits within the limits fixed-width type, U32 FW_ASSERT(buffer_size <= std::numeric_limits::max(), static_cast(buffer_size)); m_allocation_size = buffer_size; // Store the buffer size + (void)startup(); return m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds); } @@ -69,7 +70,7 @@ void TcpClientComponentImpl::connected() { // ---------------------------------------------------------------------- Drv::SendStatus TcpClientComponentImpl::send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) { - Drv::SocketIpStatus status = m_socket.send(fwBuffer.getData(), fwBuffer.getSize()); + Drv::SocketIpStatus status = send(fwBuffer.getData(), fwBuffer.getSize()); // Only deallocate buffer when the caller is not asked to retry if (status == SOCK_INTERRUPTED_TRY_AGAIN) { return SendStatus::SEND_RETRY; diff --git a/Drv/TcpClient/TcpClientComponentImpl.hpp b/Drv/TcpClient/TcpClientComponentImpl.hpp index f75c60ba4b..a8b0915321 100644 --- a/Drv/TcpClient/TcpClientComponentImpl.hpp +++ b/Drv/TcpClient/TcpClientComponentImpl.hpp @@ -14,13 +14,13 @@ #define TcpClientComponentImpl_HPP #include -#include +#include #include #include "Drv/TcpClient/TcpClientComponentAc.hpp" namespace Drv { -class TcpClientComponentImpl : public TcpClientComponentBase, public SocketReadTask { +class TcpClientComponentImpl : public TcpClientComponentBase, public SocketComponentHelper { public: // ---------------------------------------------------------------------- // Construction, initialization, and destruction diff --git a/Drv/TcpClient/test/ut/TcpClientTestMain.cpp b/Drv/TcpClient/test/ut/TcpClientTestMain.cpp index 523b9ca1f2..9f5c7e0683 100644 --- a/Drv/TcpClient/test/ut/TcpClientTestMain.cpp +++ b/Drv/TcpClient/test/ut/TcpClientTestMain.cpp @@ -4,6 +4,7 @@ #include "TcpClientTester.hpp" + TEST(Nominal, BasicMessaging) { Drv::TcpClientTester tester; tester.test_basic_messaging(); diff --git a/Drv/TcpClient/test/ut/TcpClientTester.cpp b/Drv/TcpClient/test/ut/TcpClientTester.cpp index cb4348150d..af956857a3 100644 --- a/Drv/TcpClient/test/ut/TcpClientTester.cpp +++ b/Drv/TcpClient/test/ut/TcpClientTester.cpp @@ -33,6 +33,8 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) { Drv::TcpServerSocket server; server.configure("127.0.0.1", port, 0, 100); + NATIVE_INT_TYPE client_fd = -1; + serverStat = server.startup(); this->component.configure("127.0.0.1", server.getListenPort(), 0, 100); @@ -54,25 +56,26 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) { if (not recv_thread) { status1 = this->component.open(); } else { - EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); + EXPECT_TRUE(this->wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); } - EXPECT_TRUE(this->component.getSocketHandler().isOpened()); - status2 = server.open(); + EXPECT_TRUE(this->component.isOpened()); + // fd has now been updated to be a value we need to keep track of + status2 = server.open(client_fd); EXPECT_EQ(status1, Drv::SOCK_SUCCESS); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); // If all the opens worked, then run this if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) && - (this->component.getSocketHandler().isOpened())) { + (this->component.isOpened())) { // Force the sockets not to hang, if at all possible - Drv::Test::force_recv_timeout(this->component.getSocketHandler()); - Drv::Test::force_recv_timeout(server); + Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler()); + Drv::Test::force_recv_timeout(server.m_base_fd, server); m_data_buffer.setSize(sizeof(m_data_storage)); Drv::Test::fill_random_buffer(m_data_buffer); Drv::SendStatus status = invoke_to_send(0, m_data_buffer); EXPECT_EQ(status, SendStatus::SEND_OK); - status2 = server.recv(buffer, size); + status2 = server.recv(client_fd, buffer, size); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); EXPECT_EQ(size, m_data_buffer.getSize()); Drv::Test::validate_random_buffer(m_data_buffer, buffer); @@ -80,7 +83,9 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) { if (recv_thread) { m_spinner = false; m_data_buffer.setSize(sizeof(m_data_storage)); - server.send(m_data_buffer.getData(), m_data_buffer.getSize()); + status2 = server.send(client_fd, m_data_buffer.getData(), m_data_buffer.getSize()); + EXPECT_EQ(status2, Drv::SOCK_SUCCESS); + from_deallocate_handler(0, m_data_buffer); while (not m_spinner) {} } } @@ -91,10 +96,9 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) { } else { this->component.close(); } - - server.close(); + server.close(client_fd); } - server.shutdown(); + server.shutdown(client_fd); ASSERT_from_ready_SIZE(iterations); } @@ -109,6 +113,16 @@ TcpClientTester ::TcpClientTester() TcpClientTester ::~TcpClientTester() {} +bool TcpClientTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations) { + for (U32 i = 0; i < iterations; i++) { + if (open == this->component.isOpened()) { + return true; + } + Os::Task::delay(Fw::Time(0, 10000)); + } + return false; +} + // ---------------------------------------------------------------------- // Tests // ---------------------------------------------------------------------- @@ -145,6 +159,7 @@ void TcpClientTester ::test_advanced_reconnect() { // Make sure we can get to unblocking the spinner EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size"; Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData()); + m_data_buffer.setSize(0); m_spinner = true; delete[] recvBuffer.getData(); } diff --git a/Drv/TcpClient/test/ut/TcpClientTester.hpp b/Drv/TcpClient/test/ut/TcpClientTester.hpp index 9731118939..6e6d798b76 100644 --- a/Drv/TcpClient/test/ut/TcpClientTester.hpp +++ b/Drv/TcpClient/test/ut/TcpClientTester.hpp @@ -101,6 +101,8 @@ namespace Drv { // Helper methods // ---------------------------------------------------------------------- + bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations); + //! Connect ports //! void connectPorts(); diff --git a/Drv/TcpServer/TcpServerComponentImpl.cpp b/Drv/TcpServer/TcpServerComponentImpl.cpp index 84c4ab4066..55cc14b9cc 100644 --- a/Drv/TcpServer/TcpServerComponentImpl.cpp +++ b/Drv/TcpServer/TcpServerComponentImpl.cpp @@ -22,13 +22,14 @@ namespace Drv { TcpServerComponentImpl::TcpServerComponentImpl(const char* const compName) : TcpServerComponentBase(compName), - SocketReadTask() {} + SocketComponentHelper() {} SocketIpStatus TcpServerComponentImpl::configure(const char* hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds) { - return m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds); + (void)m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds); + return startup(); } TcpServerComponentImpl::~TcpServerComponentImpl() {} @@ -65,7 +66,7 @@ void TcpServerComponentImpl::connected() { // ---------------------------------------------------------------------- Drv::SendStatus TcpServerComponentImpl::send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) { - Drv::SocketIpStatus status = m_socket.send(fwBuffer.getData(), fwBuffer.getSize()); + Drv::SocketIpStatus status = this->send(fwBuffer.getData(), fwBuffer.getSize()); // Only deallocate buffer when the caller is not asked to retry if (status == SOCK_INTERRUPTED_TRY_AGAIN) { return SendStatus::SEND_RETRY; diff --git a/Drv/TcpServer/TcpServerComponentImpl.hpp b/Drv/TcpServer/TcpServerComponentImpl.hpp index 7cf03224a6..035087f7e5 100644 --- a/Drv/TcpServer/TcpServerComponentImpl.hpp +++ b/Drv/TcpServer/TcpServerComponentImpl.hpp @@ -15,20 +15,20 @@ #include #include -#include +#include #include #include "Drv/TcpServer/TcpServerComponentAc.hpp" namespace Drv { -class TcpServerComponentImpl : public TcpServerComponentBase, public SocketReadTask { +class TcpServerComponentImpl : public TcpServerComponentBase, public SocketComponentHelper { public: // ---------------------------------------------------------------------- // Construction, initialization, and destruction // ---------------------------------------------------------------------- /** - * \brief construct the TcpClient component. + * \brief construct the TcpServer component. * \param compName: name of this component */ TcpServerComponentImpl(const char* const compName); diff --git a/Drv/TcpServer/docs/sdd.md b/Drv/TcpServer/docs/sdd.md index 5ac4746814..84f244b431 100644 --- a/Drv/TcpServer/docs/sdd.md +++ b/Drv/TcpServer/docs/sdd.md @@ -59,7 +59,6 @@ bool constructApp(bool dump, U32 port_number, char* hostname) { if (hostname != nullptr && port_number != 0) { Os::TaskString name("ReceiveTask"); comm.configure(hostname, port_number); - comm.startup(); comm.startSocketTask(name); } } diff --git a/Drv/TcpServer/test/ut/TcpServerTester.cpp b/Drv/TcpServer/test/ut/TcpServerTester.cpp index 699538d6af..c0e542f065 100644 --- a/Drv/TcpServer/test/ut/TcpServerTester.cpp +++ b/Drv/TcpServer/test/ut/TcpServerTester.cpp @@ -30,27 +30,23 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) { Drv::SocketIpStatus serverStat = Drv::SOCK_SUCCESS; U16 port = 0; - - this->component.configure("127.0.0.1", port, 0, 100); + NATIVE_INT_TYPE client_fd = -1; + status1 = this->component.configure("127.0.0.1", port, 0, 100); + EXPECT_EQ(status1, Drv::SOCK_SUCCESS); // Start up a receive thread if (recv_thread) { Os::TaskString name("receiver thread"); this->component.start(name, true, Os::Task::TASK_DEFAULT, Os::Task::TASK_DEFAULT); - EXPECT_TRUE(Drv::Test::wait_on_started(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); - } else { - serverStat = this->component.startup(); - ASSERT_EQ(serverStat, SOCK_SUCCESS) - << "TCP server startup error: " << strerror(errno) << std::endl - << "Port: " << port << std::endl; + EXPECT_TRUE(this->wait_on_started(true, Drv::Test::get_configured_delay_ms()/10 + 1)); } - EXPECT_TRUE(component.getSocketHandler().isStarted()); + EXPECT_TRUE(component.isStarted()); // Loop through a bunch of client disconnects for (U32 i = 0; i < iterations && serverStat == SOCK_SUCCESS; i++) { Drv::TcpClientSocket client; client.configure("127.0.0.1", this->component.getListenPort(), 0, 100); - status2 = client.open(); + status2 = client.open(client_fd); U32 size = sizeof(m_data_storage); @@ -58,24 +54,25 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) { if (not recv_thread) { status1 = this->component.open(); } else { - EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); + EXPECT_TRUE(this->wait_on_change(true, Drv::Test::get_configured_delay_ms()/10 + 1)); } - EXPECT_TRUE(this->component.getSocketHandler().isOpened()); + EXPECT_TRUE(this->component.isOpened()); EXPECT_EQ(status1, Drv::SOCK_SUCCESS); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); // If all the opens worked, then run this if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) && - (this->component.getSocketHandler().isOpened())) { + (this->component.isOpened())) { // Force the sockets not to hang, if at all possible - Drv::Test::force_recv_timeout(this->component.getSocketHandler()); - Drv::Test::force_recv_timeout(client); + + Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler()); + Drv::Test::force_recv_timeout(client_fd, client); m_data_buffer.setSize(sizeof(m_data_storage)); Drv::Test::fill_random_buffer(m_data_buffer); Drv::SendStatus status = invoke_to_send(0, m_data_buffer); EXPECT_EQ(status, SendStatus::SEND_OK); - status2 = client.recv(buffer, size); + status2 = client.recv(client_fd, buffer, size); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); EXPECT_EQ(size, m_data_buffer.getSize()); Drv::Test::validate_random_buffer(m_data_buffer, buffer); @@ -84,7 +81,9 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) { if (recv_thread) { m_spinner = false; m_data_buffer.setSize(sizeof(m_data_storage)); - client.send(m_data_buffer.getData(), m_data_buffer.getSize()); + status2 = client.send(client_fd, m_data_buffer.getData(), m_data_buffer.getSize()); + EXPECT_EQ(status2, Drv::SOCK_SUCCESS); + //from_deallocate_handler(0, m_data_buffer); while (not m_spinner) {} } } @@ -92,8 +91,12 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) { // Properly stop the client on the last iteration if (((1 + i) == iterations) && recv_thread) { this->component.stop(); + client.close(client_fd); // Client must be closed first or the server risks binding to an existing address + this->component.close(); + this->component.shutdown(); this->component.join(); } else { + client.close(client_fd); // Client must be closed first or the server risks binding to an existing address this->component.close(); } @@ -102,6 +105,27 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) { ASSERT_from_ready_SIZE(iterations); } +bool TcpServerTester::wait_on_change(bool open, U32 iterations) { + for (U32 i = 0; i < iterations; i++) { + if (open == this->component.isOpened()) { + return true; + } + Os::Task::delay(Fw::Time(0, 10000)); + } + return false; +} + + +bool TcpServerTester::wait_on_started(bool open, U32 iterations) { + for (U32 i = 0; i < iterations; i++) { + if (open == this->component.isStarted()) { + return true; + } + Os::Task::delay(Fw::Time(0, 10000)); + } + return false; +} + TcpServerTester ::TcpServerTester() : TcpServerGTestBase("Tester", MAX_HISTORY_SIZE), component("TcpServer"), diff --git a/Drv/TcpServer/test/ut/TcpServerTester.hpp b/Drv/TcpServer/test/ut/TcpServerTester.hpp index e43c1bf286..318fdff820 100644 --- a/Drv/TcpServer/test/ut/TcpServerTester.hpp +++ b/Drv/TcpServer/test/ut/TcpServerTester.hpp @@ -71,6 +71,9 @@ namespace Drv { // Helpers void test_with_loop(U32 iterations, bool recv_thread=false); + bool wait_on_change(bool open, U32 iterations); + bool wait_on_started(bool open, U32 iterations); + private: // ---------------------------------------------------------------------- diff --git a/Drv/Udp/UdpComponentImpl.cpp b/Drv/Udp/UdpComponentImpl.cpp index c5e90ce1dc..aa9170e738 100644 --- a/Drv/Udp/UdpComponentImpl.cpp +++ b/Drv/Udp/UdpComponentImpl.cpp @@ -24,16 +24,22 @@ namespace Drv { UdpComponentImpl::UdpComponentImpl(const char* const compName) : UdpComponentBase(compName), - SocketReadTask() {} + SocketComponentHelper() {} SocketIpStatus UdpComponentImpl::configureSend(const char* hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds) { + if (not this->isStarted()) { + (void)this->startup(); + } return m_socket.configureSend(hostname, port, send_timeout_seconds, send_timeout_microseconds); } SocketIpStatus UdpComponentImpl::configureRecv(const char* hostname, const U16 port) { + if (not this->isStarted()) { + (void)this->startup(); + } return m_socket.configureRecv(hostname, port); } @@ -71,7 +77,7 @@ void UdpComponentImpl::connected() { // ---------------------------------------------------------------------- Drv::SendStatus UdpComponentImpl::send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) { - Drv::SocketIpStatus status = m_socket.send(fwBuffer.getData(), fwBuffer.getSize()); + Drv::SocketIpStatus status = send(fwBuffer.getData(), fwBuffer.getSize()); // Always return the buffer deallocate_out(0, fwBuffer); if ((status == SOCK_DISCONNECTED) || (status == SOCK_INTERRUPTED_TRY_AGAIN)) { diff --git a/Drv/Udp/UdpComponentImpl.hpp b/Drv/Udp/UdpComponentImpl.hpp index d7399e542d..41a625472f 100644 --- a/Drv/Udp/UdpComponentImpl.hpp +++ b/Drv/Udp/UdpComponentImpl.hpp @@ -14,13 +14,13 @@ #define UdpComponentImpl_HPP #include -#include +#include #include #include "Drv/Udp/UdpComponentAc.hpp" namespace Drv { -class UdpComponentImpl : public UdpComponentBase, public SocketReadTask { +class UdpComponentImpl : public UdpComponentBase, public SocketComponentHelper { public: // ---------------------------------------------------------------------- // Construction, initialization, and destruction diff --git a/Drv/Udp/test/ut/UdpTester.cpp b/Drv/Udp/test/ut/UdpTester.cpp index a006b0e70f..de15fa7418 100644 --- a/Drv/Udp/test/ut/UdpTester.cpp +++ b/Drv/Udp/test/ut/UdpTester.cpp @@ -28,6 +28,7 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) { U8 buffer[sizeof(m_data_storage)] = {}; Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS; Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS; + NATIVE_INT_TYPE udp2_fd = -1; U16 port1 = Drv::Test::get_free_port(true); ASSERT_NE(0, port1); @@ -70,13 +71,13 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) { << "Port2: " << port2; } else { - EXPECT_TRUE(Drv::Test::wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); + EXPECT_TRUE(this->wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1)); } - EXPECT_TRUE(this->component.getSocketHandler().isOpened()); + EXPECT_TRUE(this->component.isOpened()); udp2.configureSend("127.0.0.1", port2, 0, 100); udp2.configureRecv("127.0.0.1", port1); - status2 = udp2.open(); + status2 = udp2.open(udp2_fd); EXPECT_EQ(status2, Drv::SOCK_SUCCESS) << "UDP socket open error: " << strerror(errno) << std::endl @@ -85,15 +86,15 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) { // If all the opens worked, then run this if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) && - (this->component.getSocketHandler().isOpened())) { + (this->component.isOpened())) { // Force the sockets not to hang, if at all possible - Drv::Test::force_recv_timeout(this->component.getSocketHandler()); - Drv::Test::force_recv_timeout(udp2); + Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler()); + Drv::Test::force_recv_timeout(udp2_fd, udp2); m_data_buffer.setSize(sizeof(m_data_storage)); Drv::Test::fill_random_buffer(m_data_buffer); Drv::SendStatus status = invoke_to_send(0, m_data_buffer); EXPECT_EQ(status, SendStatus::SEND_OK); - status2 = udp2.recv(buffer, size); + status2 = udp2.recv(udp2_fd, buffer, size); EXPECT_EQ(status2, Drv::SOCK_SUCCESS); EXPECT_EQ(size, m_data_buffer.getSize()); Drv::Test::validate_random_buffer(m_data_buffer, buffer); @@ -101,7 +102,7 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) { if (recv_thread) { m_spinner = false; m_data_buffer.setSize(sizeof(m_data_storage)); - udp2.send(m_data_buffer.getData(), m_data_buffer.getSize()); + udp2.send(udp2_fd, m_data_buffer.getData(), m_data_buffer.getSize()); while (not m_spinner) {} } } @@ -112,11 +113,21 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) { } else { this->component.close(); } - udp2.close(); + udp2.close(udp2_fd); } ASSERT_from_ready_SIZE(iterations); } +bool UdpTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations) { + for (U32 i = 0; i < iterations; i++) { + if (open == this->component.isOpened()) { + return true; + } + Os::Task::delay(Fw::Time(0, 10000)); + } + return false; +} + UdpTester ::UdpTester() : UdpGTestBase("Tester", MAX_HISTORY_SIZE), component("Udp"), diff --git a/Drv/Udp/test/ut/UdpTester.hpp b/Drv/Udp/test/ut/UdpTester.hpp index 14b5e7c66a..c218a13bb8 100644 --- a/Drv/Udp/test/ut/UdpTester.hpp +++ b/Drv/Udp/test/ut/UdpTester.hpp @@ -72,6 +72,8 @@ namespace Drv { // Helpers void test_with_loop(U32 iterations, bool recv_thread=false); + bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations); + private: // ---------------------------------------------------------------------- diff --git a/FppTest/CMakeLists.txt b/FppTest/CMakeLists.txt index 43cae3f2ba..1ae2e7930e 100644 --- a/FppTest/CMakeLists.txt +++ b/FppTest/CMakeLists.txt @@ -14,12 +14,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/../cmake/FPrime-Code.cmake") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/array/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/component/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/dp/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/state_machine/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/enum/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/struct/") set(SOURCE_FILES "source.cpp") set(MOD_DEPS ${PROJECT_NAME}/array ${PROJECT_NAME}/dp + ${PROJECT_NAME}/state_machine ${PROJECT_NAME}/enum ${PROJECT_NAME}/struct ${PROJECT_NAME}/component/empty diff --git a/FppTest/component/active/ActiveTest.cpp b/FppTest/component/active/ActiveTest.cpp index 6035e50318..a95180e422 100644 --- a/FppTest/component/active/ActiveTest.cpp +++ b/FppTest/component/active/ActiveTest.cpp @@ -22,16 +22,6 @@ } - void ActiveTest :: - init( - NATIVE_INT_TYPE queueDepth, - NATIVE_INT_TYPE msgSize, - NATIVE_INT_TYPE instance - ) - { - ActiveTestComponentBase::init(queueDepth, msgSize, instance); - } - ActiveTest :: ~ActiveTest() { @@ -405,6 +395,13 @@ return this->structReturnOut_out(portNum, s, sRef); } + void ActiveTest :: + enumArgsHook_handler( + const NATIVE_INT_TYPE portNum, + const FormalParamEnum &en, + FormalParamEnum &enRef + ) {} + // ---------------------------------------------------------------------- // Handler implementations for user-defined serial input ports // ---------------------------------------------------------------------- @@ -696,3 +693,16 @@ this->structInterface.args.val = str; } + // ---------------------------------------------------------------------- + // Overflow hook implementations for user-defined async ports interfaces + // ---------------------------------------------------------------------- + + void ActiveTest :: + enumArgsHook_overflowHook( + const NATIVE_INT_TYPE portNum, + const FormalParamEnum &en, + FormalParamEnum &enRef + ) + { + this->enumArgsHookOverflowed_out(portNum, en, enRef); + } diff --git a/FppTest/component/active/ActiveTest.hpp b/FppTest/component/active/ActiveTest.hpp index b984dd0d42..0998124922 100644 --- a/FppTest/component/active/ActiveTest.hpp +++ b/FppTest/component/active/ActiveTest.hpp @@ -26,13 +26,6 @@ class ActiveTest : const char* const compName //!< The component name ); - //! Initialize ActiveTest object - void init( - NATIVE_INT_TYPE queueDepth, //!< The queue depth - NATIVE_INT_TYPE msgSize, //!< The message size - NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy ActiveTest object ~ActiveTest(); @@ -262,6 +255,13 @@ class ActiveTest : FormalParamStruct& sRef //!< A struct ref ); + //! Handler implementation for enumArgsOverflow + void enumArgsHook_handler( + NATIVE_INT_TYPE portNum, //!< The port number + const FormalParamEnum& en, //!< An enum + FormalParamEnum& enRef //!< An enum ref + ); + PRIVATE: // ---------------------------------------------------------------------- @@ -444,6 +444,19 @@ class ActiveTest : const FormalParamStruct& str //!< A struct ); + PRIVATE: + + // ---------------------------------------------------------------------- + // Overflow hook implementations for user-defined async ports interfaces + // ---------------------------------------------------------------------- + + //! Overflow hook implementation for enumArgsOverflow + void enumArgsHook_overflowHook( + NATIVE_INT_TYPE portNum, //!< The port number + const FormalParamEnum& en, //!< An enum + FormalParamEnum& enRef //!< An enum ref + ); + public: //! Enables checking the serialization status of serial port invocations diff --git a/FppTest/component/active/CMakeLists.txt b/FppTest/component/active/CMakeLists.txt index fa8ffbf3bd..db5d4c4da6 100644 --- a/FppTest/component/active/CMakeLists.txt +++ b/FppTest/component/active/CMakeLists.txt @@ -51,6 +51,7 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncTesterHelpers.cpp" "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncPortTests.cpp" "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncCmdTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../tests/OverflowTests.cpp" "${CMAKE_CURRENT_LIST_DIR}/../types/FormalParamTypes.cpp" "${CMAKE_CURRENT_LIST_DIR}/../../utils/Utils.cpp" ) diff --git a/FppTest/component/active/active.fpp b/FppTest/component/active/active.fpp index dfe555cb88..6b843994e4 100644 --- a/FppTest/component/active/active.fpp +++ b/FppTest/component/active/active.fpp @@ -8,6 +8,8 @@ active component ActiveTest { include "../include/special_ports.fppi" include "../include/internal_ports.fppi" + include "../include/output_ports.fppi" + include "../include/commands.fppi" include "../include/commands_async.fppi" include "../include/events.fppi" diff --git a/FppTest/component/active/test/ut/Tester.cpp b/FppTest/component/active/test/ut/Tester.cpp index c1e1308e74..64f60796fb 100644 --- a/FppTest/component/active/test/ut/Tester.cpp +++ b/FppTest/component/active/test/ut/Tester.cpp @@ -4,8 +4,9 @@ // \brief cpp file for ActiveTest test harness implementation class // ====================================================================== -#include "STest/Pick/Pick.hpp" #include "Tester.hpp" +#include "STest/Pick/Pick.hpp" + // ---------------------------------------------------------------------- // Construction and destruction diff --git a/FppTest/component/active/test/ut/Tester.hpp b/FppTest/component/active/test/ut/Tester.hpp index 117df5b272..c323c9b62c 100644 --- a/FppTest/component/active/test/ut/Tester.hpp +++ b/FppTest/component/active/test/ut/Tester.hpp @@ -19,6 +19,7 @@ #include "FppTest/component/tests/TlmTests.hpp" #include "FppTest/component/types/FormalParamTypes.hpp" + class Tester : public ActiveTestGTestBase { // ---------------------------------------------------------------------- // Construction and destruction @@ -62,6 +63,12 @@ class Tester : public ActiveTestGTestBase { void testTime(); + void testOverflowAssert(); + + void testOverflowDrop(); + + void testOverflowHook(); + PRIVATE: // ---------------------------------------------------------------------- // Handlers for typed from ports @@ -185,6 +192,10 @@ class Tester : public ActiveTestGTestBase { */ ) final; + void from_enumArgsHookOverflowed_handler(const NATIVE_INT_TYPE portNum, + const FormalParamEnum& en, + FormalParamEnum& enRef); + PRIVATE: // ---------------------------------------------------------------------- // Handlers for serial from ports diff --git a/FppTest/component/empty/Empty.cpp b/FppTest/component/empty/Empty.cpp index a43b619f80..d4fac37831 100644 --- a/FppTest/component/empty/Empty.cpp +++ b/FppTest/component/empty/Empty.cpp @@ -21,14 +21,6 @@ Empty :: } -void Empty :: - init( - NATIVE_INT_TYPE instance - ) -{ - EmptyComponentBase::init(instance); -} - Empty :: ~Empty() { diff --git a/FppTest/component/empty/Empty.hpp b/FppTest/component/empty/Empty.hpp index 705d9b6275..2b7fcdbd43 100644 --- a/FppTest/component/empty/Empty.hpp +++ b/FppTest/component/empty/Empty.hpp @@ -24,11 +24,6 @@ class Empty : const char* const compName //!< The component name ); - //! Initialize Empty object - void init( - NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy Empty object ~Empty(); diff --git a/FppTest/component/include/output_ports.fppi b/FppTest/component/include/output_ports.fppi new file mode 100644 index 0000000000..74dbbed057 --- /dev/null +++ b/FppTest/component/include/output_ports.fppi @@ -0,0 +1 @@ +output port enumArgsHookOverflowed: [2] EnumArgs diff --git a/FppTest/component/include/typed_ports_async.fppi b/FppTest/component/include/typed_ports_async.fppi index ed5c7380f2..dc41069781 100644 --- a/FppTest/component/include/typed_ports_async.fppi +++ b/FppTest/component/include/typed_ports_async.fppi @@ -9,3 +9,5 @@ async input port enumArgsAsync: [2] EnumArgs assert async input port arrayArgsAsync: [2] ArrayArgs priority 10 block async input port structArgsAsync: [2] StructArgs priority 5 drop + +async input port enumArgsHook: [2] EnumArgs hook diff --git a/FppTest/component/passive/PassiveTest.cpp b/FppTest/component/passive/PassiveTest.cpp index efde416db1..faadec4cff 100644 --- a/FppTest/component/passive/PassiveTest.cpp +++ b/FppTest/component/passive/PassiveTest.cpp @@ -21,14 +21,6 @@ } - void PassiveTest :: - init( - NATIVE_INT_TYPE instance - ) - { - PassiveTestComponentBase::init(instance); - } - PassiveTest :: ~PassiveTest() { diff --git a/FppTest/component/passive/PassiveTest.hpp b/FppTest/component/passive/PassiveTest.hpp index 386e054fde..c36a93cea0 100644 --- a/FppTest/component/passive/PassiveTest.hpp +++ b/FppTest/component/passive/PassiveTest.hpp @@ -26,11 +26,6 @@ class PassiveTest : const char* const compName //!< The component name ); - //! Initialize PassiveTest object - void init( - NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy PassiveTest object ~PassiveTest(); diff --git a/FppTest/component/queued/CMakeLists.txt b/FppTest/component/queued/CMakeLists.txt index d7bd850385..540b9ba021 100644 --- a/FppTest/component/queued/CMakeLists.txt +++ b/FppTest/component/queued/CMakeLists.txt @@ -45,6 +45,7 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncTesterHelpers.cpp" "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncPortTests.cpp" "${CMAKE_CURRENT_LIST_DIR}/../tests/AsyncCmdTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../tests/OverflowTests.cpp" "${CMAKE_CURRENT_LIST_DIR}/../types/FormalParamTypes.cpp" "${CMAKE_CURRENT_LIST_DIR}/../../utils/Utils.cpp" ) diff --git a/FppTest/component/queued/QueuedTest.cpp b/FppTest/component/queued/QueuedTest.cpp index 4733b735f9..6158b8f02e 100644 --- a/FppTest/component/queued/QueuedTest.cpp +++ b/FppTest/component/queued/QueuedTest.cpp @@ -22,16 +22,6 @@ } - void QueuedTest :: - init( - NATIVE_INT_TYPE queueDepth, - NATIVE_INT_TYPE msgSize, - NATIVE_INT_TYPE instance - ) - { - QueuedTestComponentBase::init(queueDepth, msgSize, instance); - } - QueuedTest :: ~QueuedTest() { @@ -405,6 +395,15 @@ return this->structReturnOut_out(portNum, s, sRef); } + + void QueuedTest :: + enumArgsHook_handler( + const NATIVE_INT_TYPE portNum, + const FormalParamEnum &en, + FormalParamEnum &enRef + ) + {} + // ---------------------------------------------------------------------- // Handler implementations for user-defined serial input ports // ---------------------------------------------------------------------- @@ -696,3 +695,17 @@ this->structInterface.args.val = str; } + + // ---------------------------------------------------------------------- + // Overflow hook implementations for user-defined async ports interfaces + // ---------------------------------------------------------------------- + + void QueuedTest :: + enumArgsHook_overflowHook( + const NATIVE_INT_TYPE portNum, + const FormalParamEnum &en, + FormalParamEnum &enRef + ) + { + this->enumArgsHookOverflowed_out(portNum, en, enRef); + } diff --git a/FppTest/component/queued/QueuedTest.hpp b/FppTest/component/queued/QueuedTest.hpp index dc8c2a453d..558c12c5be 100644 --- a/FppTest/component/queued/QueuedTest.hpp +++ b/FppTest/component/queued/QueuedTest.hpp @@ -26,13 +26,6 @@ class QueuedTest : const char* const compName //!< The component name ); - //! Initialize QueuedTest object - void init( - NATIVE_INT_TYPE queueDepth, //!< The queue depth - NATIVE_INT_TYPE msgSize, //!< The message size - NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy QueuedTest object ~QueuedTest(); @@ -262,6 +255,13 @@ class QueuedTest : FormalParamStruct& sRef //!< A struct ref ); + //! Handler implementation for enumArgsOverflow + void enumArgsHook_handler( + NATIVE_INT_TYPE portNum, //!< The port number + const FormalParamEnum& en, //!< An enum + FormalParamEnum& enRef //!< An enum ref + ); + PRIVATE: // ---------------------------------------------------------------------- @@ -444,6 +444,20 @@ class QueuedTest : const FormalParamStruct& str //!< A struct ); + PRIVATE: + + // ---------------------------------------------------------------------- + // Overflow hook implementations for user-defined async ports interfaces + // ---------------------------------------------------------------------- + + //! Overflow hook implementation for enumArgsOverflow + void enumArgsHook_overflowHook( + NATIVE_INT_TYPE portNum, //!< The port number + const FormalParamEnum& en, //!< An enum + FormalParamEnum& enRef //!< An enum ref + ); + + public: //! Enables checking the serialization status of serial port invocations diff --git a/FppTest/component/queued/queued.fpp b/FppTest/component/queued/queued.fpp index 7cc8274a6a..d1d262abe0 100644 --- a/FppTest/component/queued/queued.fpp +++ b/FppTest/component/queued/queued.fpp @@ -8,6 +8,8 @@ queued component QueuedTest { include "../include/special_ports.fppi" include "../include/internal_ports.fppi" + include "../include/output_ports.fppi" + include "../include/commands.fppi" include "../include/commands_async.fppi" include "../include/events.fppi" diff --git a/FppTest/component/queued/test/ut/Tester.hpp b/FppTest/component/queued/test/ut/Tester.hpp index c13d839eeb..05d19bfa9c 100644 --- a/FppTest/component/queued/test/ut/Tester.hpp +++ b/FppTest/component/queued/test/ut/Tester.hpp @@ -62,6 +62,13 @@ class Tester : public QueuedTestGTestBase { void testTime(); + + void testOverflowAssert(); + + void testOverflowDrop(); + + void testOverflowHook(); + PRIVATE: // ---------------------------------------------------------------------- // Handlers for typed from ports @@ -185,6 +192,10 @@ class Tester : public QueuedTestGTestBase { */ ); + void from_enumArgsHookOverflowed_handler(const NATIVE_INT_TYPE portNum, + const FormalParamEnum& en, + FormalParamEnum& enRef); + PRIVATE: // ---------------------------------------------------------------------- // Handlers for serial from ports diff --git a/FppTest/component/tests/AsyncTesterHelpers.cpp b/FppTest/component/tests/AsyncTesterHelpers.cpp index 7d2d201d53..8d251529d2 100644 --- a/FppTest/component/tests/AsyncTesterHelpers.cpp +++ b/FppTest/component/tests/AsyncTesterHelpers.cpp @@ -23,6 +23,11 @@ void Tester ::connectAsyncPorts() { this->connect_to_enumArgsAsync(i, this->component.get_enumArgsAsync_InputPort(i)); } + // enumArgsHook + for (NATIVE_INT_TYPE i = 0; i < 2; ++i) { + this->connect_to_enumArgsHook(i, this->component.get_enumArgsHook_InputPort(i)); + } + // noArgsAsync for (NATIVE_INT_TYPE i = 0; i < 2; ++i) { this->connect_to_noArgsAsync(i, this->component.get_noArgsAsync_InputPort(i)); @@ -56,6 +61,11 @@ void Tester ::connectAsyncPorts() { // serialAsyncDropPriority this->connect_to_serialAsyncDropPriority(0, this->component.get_serialAsyncDropPriority_InputPort(0)); + + // enumArgsHookOverflowed + for (FwIndexType i = 0; i < 2; i++) { + this->component.set_enumArgsHookOverflowed_OutputPort(i, this->get_from_enumArgsHookOverflowed(i)); + } } Fw::QueuedComponentBase::MsgDispatchStatus Tester ::doDispatch() { diff --git a/FppTest/component/tests/AsyncTests.cpp b/FppTest/component/tests/AsyncTests.cpp index ed58ad4d5c..cfc2837759 100644 --- a/FppTest/component/tests/AsyncTests.cpp +++ b/FppTest/component/tests/AsyncTests.cpp @@ -53,3 +53,19 @@ using InternalInterfaceTestImplementations = ::testing::Types; INSTANTIATE_TYPED_TEST_SUITE_P(FppTest, ComponentInternalInterfaceTest, InternalInterfaceTestImplementations); + +TEST(ComponentOverflow, OverflowHook) { + Tester tester; + tester.testOverflowHook(); +} + +TEST(ComponentOverflow, OverflowDrop) { + Tester tester; + tester.testOverflowDrop(); +} + +TEST(ComponentOverflow, OverflowAssert) { + Tester tester; + tester.testOverflowAssert(); +} + diff --git a/FppTest/component/tests/OverflowTests.cpp b/FppTest/component/tests/OverflowTests.cpp new file mode 100644 index 0000000000..4128d0fc32 --- /dev/null +++ b/FppTest/component/tests/OverflowTests.cpp @@ -0,0 +1,75 @@ +// ====================================================================== +// \title OverflowTests.cpp +// \author mstarch +// \brief cpp file for overflow tests +// +// \copyright +// Copyright (C) 2009-2023 California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#include "Fw/Time/Time.hpp" +#include "STest/Pick/Pick.hpp" +#include "Tester.hpp" + +// ---------------------------------------------------------------------- +// Overflow assert test +// ---------------------------------------------------------------------- + +void Tester ::testOverflowAssert() { + FormalParamEnum x = FormalParamEnum::T::X; + FormalParamEnum y = FormalParamEnum::T::Y; + FormalParamEnum z = FormalParamEnum::T::Z; + + for (FwSizeType i = 0; i < Tester::TEST_INSTANCE_QUEUE_DEPTH; i++) { + this->invoke_to_enumArgsAsync(i % 2, x, y); + } + ASSERT_DEATH_IF_SUPPORTED(this->invoke_to_enumArgsAsync(0, y, z), ""); +} + +// ---------------------------------------------------------------------- +// Overflow drop test +// ---------------------------------------------------------------------- + +void Tester ::testOverflowDrop() { + FormalParamStruct x; + FormalParamStruct y; + FormalParamStruct z; + for (FwSizeType i = 0; i < Tester::TEST_INSTANCE_QUEUE_DEPTH; i++) { + this->invoke_to_structArgsAsync(i % 2, x, y); + } + // This will overflow and should not crash + this->invoke_to_structArgsAsync(0, y, z); +} + +// ---------------------------------------------------------------------- +// Overflow hook test +// ---------------------------------------------------------------------- + +void Tester ::testOverflowHook() { + FormalParamEnum x = FormalParamEnum::T::X; + FormalParamEnum y = FormalParamEnum::T::Y; + FormalParamEnum z = FormalParamEnum::T::Z; + + for (FwSizeType i = 0; i < Tester::TEST_INSTANCE_QUEUE_DEPTH; i++) { + this->invoke_to_enumArgsHook(i % 2, x, y); + } + this->invoke_to_enumArgsHook(0, y, z); + this->invoke_to_enumArgsHook(1, z, x); + ASSERT_from_enumArgsHookOverflowed_SIZE(2); + ASSERT_from_enumArgsHookOverflowed(0, y, z); + ASSERT_from_enumArgsHookOverflowed(1, z, x); + +} + +// ---------------------------------------------------------------------- +// Handler to support overflow hook test +// ---------------------------------------------------------------------- + +void Tester ::from_enumArgsHookOverflowed_handler(const NATIVE_INT_TYPE portNum, + const FormalParamEnum& en, + FormalParamEnum& enRef) { + this->pushFromPortEntry_enumArgsHookOverflowed(en, enRef); +} diff --git a/FppTest/dp/DpTest.cpp b/FppTest/dp/DpTest.cpp index 7fe41b8690..56d0e7baa5 100644 --- a/FppTest/dp/DpTest.cpp +++ b/FppTest/dp/DpTest.cpp @@ -36,10 +36,6 @@ DpTest::DpTest(const char* const compName, } } -void DpTest ::init(const NATIVE_INT_TYPE queueDepth, const NATIVE_INT_TYPE instance) { - DpTestComponentBase::init(queueDepth, instance); -} - DpTest ::~DpTest() {} // ---------------------------------------------------------------------- diff --git a/FppTest/dp/DpTest.hpp b/FppTest/dp/DpTest.hpp index 490d2eecbf..1adab4e75b 100644 --- a/FppTest/dp/DpTest.hpp +++ b/FppTest/dp/DpTest.hpp @@ -66,11 +66,6 @@ class DpTest : public DpTestComponentBase { const Fw::StringBase& stringRecordData //!< The StringRecord data ); - //! Initialize object DpTest - void init(const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy object DpTest ~DpTest(); diff --git a/FppTest/settings.ini b/FppTest/settings.ini new file mode 100644 index 0000000000..4abbf89e7f --- /dev/null +++ b/FppTest/settings.ini @@ -0,0 +1,5 @@ +; Ref requires no specific settings thus the [fprime] configuration block is empty +; For more information: https://nasa.github.io/fprime/UsersGuide/user/settings.html +[fprime] +framework_path: .. +default_cmake_options: FPRIME_SKIP_TOOLS_VERSION_CHECK=ON FPRIME_ENABLE_FRAMEWORK_UTS=OFF FPRIME_ENABLE_AUTOCODER_UTS=OFF diff --git a/FppTest/state_machine/CMakeLists.txt b/FppTest/state_machine/CMakeLists.txt new file mode 100644 index 0000000000..6d1e6c1e0f --- /dev/null +++ b/FppTest/state_machine/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/SmTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/SmTest.fpp" + "${CMAKE_CURRENT_LIST_DIR}/DeviceSm.cpp" + "${CMAKE_CURRENT_LIST_DIR}/HackSm.cpp" +) +set(MOD_DEPS Fw/Sm) + +register_fprime_module() + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/SmTest.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/SmTestTestMain.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/SmTestTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/DeviceSm.cpp" + "${CMAKE_CURRENT_LIST_DIR}/HackSm.cpp" +) + +set(UT_MOD_DEPS STest) +set(UT_AUTO_HELPERS ON) +register_fprime_ut() diff --git a/FppTest/state_machine/DeviceSm.cpp b/FppTest/state_machine/DeviceSm.cpp new file mode 100644 index 0000000000..5fce5cf81c --- /dev/null +++ b/FppTest/state_machine/DeviceSm.cpp @@ -0,0 +1,74 @@ + +// ====================================================================== +// \title DeviceSm.cpp +// \author Auto-generated +// \brief cpp file for state machine DeviceSm +// +// ====================================================================== + +#include +#include "DeviceSm.hpp" + + +void FppTest::DeviceSm::init(const FwEnumStoreType stateMachineId) +{ + parent->DeviceSm_turnOff(stateMachineId); + this->state = OFF; + +} + + +void FppTest::DeviceSm::update( + const FwEnumStoreType stateMachineId, + const DeviceSm_Interface::DeviceSm_Signals signal, + const Fw::SmSignalBuffer &data +) +{ + switch (this->state) { + + /** + * state OFF + */ + case OFF: + + switch (signal) { + + case DeviceSm_Interface::DeviceSm_Signals::RTI_SIG: + if ( parent->DeviceSm_g1(stateMachineId) ) { + parent->DeviceSm_a1(stateMachineId, signal, data); + parent->DeviceSm_turnOn(stateMachineId); + this->state = ON; + } + + break; + + default: + break; + } + break; + + /** + * state ON + */ + case ON: + + switch (signal) { + + case DeviceSm_Interface::DeviceSm_Signals::RTI_SIG: + if (parent->DeviceSm_g2(stateMachineId, signal, data) ) { + parent->DeviceSm_a2(stateMachineId); + parent->DeviceSm_turnOff(stateMachineId); + this->state = OFF; + } + + break; + + default: + break; + } + break; + + default: + FW_ASSERT(0); + } +} diff --git a/FppTest/state_machine/DeviceSm.fppi b/FppTest/state_machine/DeviceSm.fppi new file mode 100644 index 0000000000..a506e51c3a --- /dev/null +++ b/FppTest/state_machine/DeviceSm.fppi @@ -0,0 +1,7 @@ + + + enum DeviceSmStates { + OFF = 0 + ON = 1 + } + diff --git a/FppTest/state_machine/DeviceSm.hpp b/FppTest/state_machine/DeviceSm.hpp new file mode 100644 index 0000000000..c9592b07fd --- /dev/null +++ b/FppTest/state_machine/DeviceSm.hpp @@ -0,0 +1,76 @@ + +// ====================================================================== +// \title DeviceSm.h +// \author Auto-generated +// \brief header file for state machine DeviceSm +// +// ====================================================================== + +#ifndef DEVICESM_H_ +#define DEVICESM_H_ + +#include +#include + +namespace FppTest { + +class DeviceSm_Interface { + public: + enum DeviceSm_Signals { + RTI_SIG, + }; + + + virtual bool DeviceSm_g1(const FwEnumStoreType stateMachineId) = 0; + + + virtual bool DeviceSm_g2( + const FwEnumStoreType stateMachineId, + const DeviceSm_Interface::DeviceSm_Signals signal, + const Fw::SmSignalBuffer &data) = 0; + + + virtual void DeviceSm_turnOff(const FwEnumStoreType stateMachineId) = 0; + + + virtual void DeviceSm_a1( + const FwEnumStoreType stateMachineId, + const DeviceSm_Interface::DeviceSm_Signals signal, + const Fw::SmSignalBuffer &data) = 0; + + + virtual void DeviceSm_turnOn(const FwEnumStoreType stateMachineId) = 0; + + + virtual void DeviceSm_a2(const FwEnumStoreType stateMachineId) = 0; + + +}; + +class DeviceSm { + + private: + DeviceSm_Interface *parent; + + public: + + DeviceSm(DeviceSm_Interface* parent) : parent(parent) {} + + enum DeviceSm_States { + OFF, + ON, + }; + + enum DeviceSm_States state; + + void init(const FwEnumStoreType stateMachineId); + void update( + const FwEnumStoreType stateMachineId, + const DeviceSm_Interface::DeviceSm_Signals signal, + const Fw::SmSignalBuffer &data + ); +}; + +} + +#endif diff --git a/FppTest/state_machine/DeviceSm.plantuml b/FppTest/state_machine/DeviceSm.plantuml new file mode 100644 index 0000000000..48a3e38645 --- /dev/null +++ b/FppTest/state_machine/DeviceSm.plantuml @@ -0,0 +1,16 @@ + +@startuml + +[*] --> OFF + +state OFF { + OFF::Entry: turnOff() +} + +state ON { + ON::Entry: turnOn() +} + +OFF --> ON : RTI [g1()]/a1(e) +ON --> OFF : RTI [g2(e)]/a2() +@enduml diff --git a/FppTest/state_machine/HackSm.cpp b/FppTest/state_machine/HackSm.cpp new file mode 100644 index 0000000000..6cf444f0ca --- /dev/null +++ b/FppTest/state_machine/HackSm.cpp @@ -0,0 +1,98 @@ + +// ====================================================================== +// \title HackSm.cpp +// \author Auto-generated +// \brief cpp file for state machine HackSm +// +// ====================================================================== + +#include +#include "HackSm.hpp" + + +void FppTest::HackSm::init(const FwEnumStoreType stateMachineId) +{ + parent->HackSm_turnOff(stateMachineId); + this->state = OFF; + +} + + +void FppTest::HackSm::update( + const FwEnumStoreType stateMachineId, + const HackSm_Interface::HackSm_Signals signal, + const Fw::SmSignalBuffer &data +) +{ + switch (this->state) { + + /** + * state OFF + */ + case OFF: + + switch (signal) { + + case HackSm_Interface::HackSm_Signals::RTI_SIG: + parent->HackSm_turnOn(stateMachineId); + this->state = ON; + + break; + + case HackSm_Interface::HackSm_Signals::CHECK_SIG: + parent->HackSm_doDiag(stateMachineId); + this->state = DIAG; + + break; + + default: + break; + } + break; + + /** + * state ON + */ + case ON: + + switch (signal) { + + case HackSm_Interface::HackSm_Signals::RTI_SIG: + parent->HackSm_turnOff(stateMachineId); + this->state = OFF; + + break; + + case HackSm_Interface::HackSm_Signals::CHECK_SIG: + parent->HackSm_doDiag(stateMachineId); + this->state = DIAG; + + break; + + default: + break; + } + break; + + /** + * state DIAG + */ + case DIAG: + + switch (signal) { + + case HackSm_Interface::HackSm_Signals::RTI_SIG: + parent->HackSm_turnOff(stateMachineId); + this->state = OFF; + + break; + + default: + break; + } + break; + + default: + FW_ASSERT(0); + } +} diff --git a/FppTest/state_machine/HackSm.hpp b/FppTest/state_machine/HackSm.hpp new file mode 100644 index 0000000000..7cfa698ad2 --- /dev/null +++ b/FppTest/state_machine/HackSm.hpp @@ -0,0 +1,63 @@ + +// ====================================================================== +// \title HackSm.h +// \author Auto-generated +// \brief header file for state machine HackSm +// +// ====================================================================== + +#ifndef HACKSM_H_ +#define HACKSM_H_ + +#include +#include + +namespace FppTest { + +class HackSm_Interface { + public: + enum HackSm_Signals { + RTI_SIG, + CHECK_SIG, + }; + + + virtual void HackSm_turnOff(const FwEnumStoreType stateMachineId) = 0; + + + virtual void HackSm_turnOn(const FwEnumStoreType stateMachineId) = 0; + + + virtual void HackSm_doDiag(const FwEnumStoreType stateMachineId) = 0; + + +}; + +class HackSm { + + private: + HackSm_Interface *parent; + + public: + + HackSm(HackSm_Interface* parent) : parent(parent) {} + + enum HackSm_States { + OFF, + ON, + DIAG, + }; + + enum HackSm_States state; + + void init(const FwEnumStoreType stateMachineId); + void update( + const FwEnumStoreType stateMachineId, + const HackSm_Interface::HackSm_Signals signal, + const Fw::SmSignalBuffer &data + ); +}; + +} + +#endif diff --git a/FppTest/state_machine/HackSm.plantuml b/FppTest/state_machine/HackSm.plantuml new file mode 100644 index 0000000000..3c02ab7a76 --- /dev/null +++ b/FppTest/state_machine/HackSm.plantuml @@ -0,0 +1,23 @@ + +@startuml + +[*] --> OFF + +state OFF { + OFF::Entry: turnOff() +} + +state ON { + ON::Entry: turnOn() +} + +state DIAG { + DIAG::Entry: doDiag() +} + +OFF --> ON : RTI +ON --> OFF : RTI +ON --> DIAG : CHECK +OFF --> DIAG : CHECK +DIAG --> OFF : RTI +@enduml diff --git a/FppTest/state_machine/Makefile b/FppTest/state_machine/Makefile new file mode 100644 index 0000000000..8784d9e203 --- /dev/null +++ b/FppTest/state_machine/Makefile @@ -0,0 +1,3 @@ +HOME_DIR := $(HOME) +autocode: + $(HOME_DIR)/STARS/autocoder/Stars.py -noImpl -backend fprime -namespace FppTest -model DeviceSm.plantuml diff --git a/FppTest/state_machine/SMEvents.hpp b/FppTest/state_machine/SMEvents.hpp new file mode 100644 index 0000000000..161053a135 --- /dev/null +++ b/FppTest/state_machine/SMEvents.hpp @@ -0,0 +1 @@ +#include "Fw/Types/SMEventsSerializableAc.hpp" diff --git a/FppTest/state_machine/SmTest.cpp b/FppTest/state_machine/SmTest.cpp new file mode 100644 index 0000000000..776071bf89 --- /dev/null +++ b/FppTest/state_machine/SmTest.cpp @@ -0,0 +1,101 @@ +// ====================================================================== +// \title SmTest.cpp +// \author watney +// \brief hpp file for SmTest component implementation class +// ====================================================================== + +#include + +#include "FppTest/state_machine/SmTest.hpp" +#include "Fw/Types/Assert.hpp" + +namespace FppTest { + +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- + +SmTest::SmTest(const char* const compName): + SmTestComponentBase(compName) {} + +SmTest ::~SmTest() {} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void SmTest::schedIn_handler(const NATIVE_INT_TYPE portNum, U32 context) { + Fw::SmSignalBuffer data; + + device1_stateMachineInvoke(DeviceSm_Interface::DeviceSm_Signals::RTI_SIG, data); + device2_stateMachineInvoke(DeviceSm_Interface::DeviceSm_Signals::RTI_SIG, data); + device3_stateMachineInvoke(HackSm_Interface::HackSm_Signals::RTI_SIG, data); + device4_stateMachineInvoke(HackSm_Interface::HackSm_Signals::RTI_SIG, data); + device5_stateMachineInvoke(HackSm_Interface::HackSm_Signals::RTI_SIG, data); + +} + +//! Overflow hook for state machine device4 +void SmTest::device4_stateMachineOverflowHook( + const HackSm_Interface::HackSm_Signals signal, //!< The state machine signal + const Fw::SmSignalBuffer& data //!< The state machine data +) { + +} + +void SmTest::DeviceSm_turnOn(const FwEnumStoreType stateMachineId) { + printf("DeviceSm turnOn for state machine %d\n", stateMachineId); +} + +void SmTest::DeviceSm_turnOff(const FwEnumStoreType stateMachineId) { + printf("DeviceSm turnOff for state machine %d\n", stateMachineId); +} + +void SmTest::DeviceSm_a1( + const FwEnumStoreType stateMachineId, + const DeviceSm_Signals signal, + const Fw::SmSignalBuffer& data + ) { + printf("Action 1, stateMachineId = %d, signal = %d\n", stateMachineId, signal); +} + + + bool SmTest::DeviceSm_g1(const FwEnumStoreType stateMachineId) { + return true; + } + + bool SmTest::DeviceSm_g2( + const FwEnumStoreType stateMachineId, + const DeviceSm_Signals signal, + const Fw::SmSignalBuffer& data + ) { + return true; + } + +void SmTest::DeviceSm_a2(const FwEnumStoreType stateMachineId) { + printf("Action 2\n"); +} + +void SmTest::HackSm_turnOn(const FwEnumStoreType stateMachineId) { + printf("HackSm turn on\n"); +} + +void SmTest::HackSm_turnOff(const FwEnumStoreType stateMachineId) { + printf("HackSm turn off\n"); +} + +void SmTest::HackSm_doDiag(const FwEnumStoreType stateMachineId) { + printf("HackSm do diag\n"); +} + + +// ---------------------------------------------------------------------- +// Data product handler implementations +// ---------------------------------------------------------------------- + + +// ---------------------------------------------------------------------- +// Private helper functions +// ---------------------------------------------------------------------- + +} // end namespace FppTest diff --git a/FppTest/state_machine/SmTest.fpp b/FppTest/state_machine/SmTest.fpp new file mode 100644 index 0000000000..c62cb7c62f --- /dev/null +++ b/FppTest/state_machine/SmTest.fpp @@ -0,0 +1,30 @@ + +module FppTest { + + state machine DeviceSm + state machine HackSm + + @ A component for testing data product code gen + active component SmTest { + + # ---------------------------------------------------------------------- + # Types + # ---------------------------------------------------------------------- + + + # ---------------------------------------------------------------------- + # General ports + # ---------------------------------------------------------------------- + + @ A schedIn port to run the data product generation + async input port schedIn: Svc.Sched + + state machine instance device1: DeviceSm priority 1 block + state machine instance device2: DeviceSm priority 2 assert + state machine instance device3: HackSm priority 3 drop + state machine instance device4: HackSm priority 4 hook + state machine instance device5: HackSm + + } + +} diff --git a/FppTest/state_machine/SmTest.hpp b/FppTest/state_machine/SmTest.hpp new file mode 100644 index 0000000000..55f7dec146 --- /dev/null +++ b/FppTest/state_machine/SmTest.hpp @@ -0,0 +1,121 @@ +// ====================================================================== +// \title SmTest.hpp +// \author watney +// \brief hpp file for SmTest component implementation class +// ====================================================================== + +#ifndef FppTest_SmTest_HPP +#define FppTest_SmTest_HPP + +#include + +#include "FppTest/state_machine/SmTestComponentAc.hpp" +#include "Fw/Types/String.hpp" + +namespace FppTest { + +class SmTest : + public SmTestComponentBase +{ + + // Friend class for testing + friend class Tester; + + public: + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + + + public: + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- + + + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object SmTest + SmTest(const char* const compName); //!< The component name + + //! Destroy object SmTest + ~SmTest(); + + public: + // ---------------------------------------------------------------------- + // Public interface methods + // ---------------------------------------------------------------------- + + + + private: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for schedIn + void schedIn_handler(const NATIVE_INT_TYPE portNum, //!< The port number + U32 context //!< The call order + ) final; + + //! Overflow hook for state machine device4 + void device4_stateMachineOverflowHook( + const HackSm_Interface::HackSm_Signals signal, //!< The state machine signal + const Fw::SmSignalBuffer& data //!< The state machine data + ); + + // State machine functions + void DeviceSm_turnOn(const FwEnumStoreType stateMachineId); + + void DeviceSm_turnOff(const FwEnumStoreType stateMachineId); + + void DeviceSm_a1( + const FwEnumStoreType stateMachineId, + const DeviceSm_Signals signal, + const Fw::SmSignalBuffer& data + ); + + void DeviceSm_a2(const FwEnumStoreType stateMachineId); + + bool DeviceSm_g1(const FwEnumStoreType stateMachineId); + + bool DeviceSm_g2( + const FwEnumStoreType stateMachineId, + const DeviceSm_Signals signal, + const Fw::SmSignalBuffer& data + ); + + void HackSm_turnOn(const FwEnumStoreType stateMachineId); + + void HackSm_turnOff(const FwEnumStoreType stateMachineId); + + void HackSm_doDiag(const FwEnumStoreType stateMachineId); + + private: + // ---------------------------------------------------------------------- + // Data product handler implementations + // ---------------------------------------------------------------------- + + + private: + // ---------------------------------------------------------------------- + // Private helper functions + // ---------------------------------------------------------------------- + + + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + +}; + +} // end namespace FppTest + +#endif diff --git a/FppTest/state_machine/test/ut/SmTestTestMain.cpp b/FppTest/state_machine/test/ut/SmTestTestMain.cpp new file mode 100644 index 0000000000..769b464f3d --- /dev/null +++ b/FppTest/state_machine/test/ut/SmTestTestMain.cpp @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------- +// SmTestTestMain.cpp +// ---------------------------------------------------------------------- + +#include "FppTest/state_machine/test/ut/SmTestTester.hpp" +#include "Fw/Test/UnitTest.hpp" +#include "STest/Random/Random.hpp" + +using namespace FppTest; + +TEST(schedIn, OK) { + COMMENT("schedIn OK"); + SmTestTester tester; + tester.schedIn_OK(); +} + + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + STest::Random::seed(); + return RUN_ALL_TESTS(); +} diff --git a/FppTest/state_machine/test/ut/SmTestTester.cpp b/FppTest/state_machine/test/ut/SmTestTester.cpp new file mode 100644 index 0000000000..ebca73c4a0 --- /dev/null +++ b/FppTest/state_machine/test/ut/SmTestTester.cpp @@ -0,0 +1,80 @@ +// ====================================================================== +// \title SmTestTester.cpp +// \author watney +// \brief cpp file for SmTest test harness implementation class +// ====================================================================== + +#include + +#include "FppTest/state_machine/test/ut/SmTestTester.hpp" +#include "Fw/Types/ExternalString.hpp" +#include "STest/Pick/Pick.hpp" + +namespace FppTest { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +SmTestTester::SmTestTester() + : SmTestGTestBase("SmTestTester", SmTestTester::MAX_HISTORY_SIZE), + component("SmTest") { + this->initComponents(); + this->connectPorts(); + this->component.setIdBase(ID_BASE); +} + +SmTestTester::~SmTestTester() {} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- + +void SmTestTester::schedIn_OK() { + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device1.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device2.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device3.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device4.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device5.state); + invoke_to_schedIn(0,0); + dispatchAll(); + ASSERT_EQ(DeviceSm::ON, this->component.m_stateMachine_device1.state); + ASSERT_EQ(DeviceSm::ON, this->component.m_stateMachine_device2.state); + ASSERT_EQ(DeviceSm::ON, this->component.m_stateMachine_device3.state); + ASSERT_EQ(DeviceSm::ON, this->component.m_stateMachine_device4.state); + ASSERT_EQ(DeviceSm::ON, this->component.m_stateMachine_device5.state); + invoke_to_schedIn(0,0); + dispatchAll(); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device1.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device2.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device3.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device4.state); + ASSERT_EQ(DeviceSm::OFF, this->component.m_stateMachine_device5.state); + + Fw::SmSignalBuffer data; + this->component.device3_stateMachineInvoke(HackSm_Interface::HackSm_Signals::CHECK_SIG, data); + dispatchAll(); + ASSERT_EQ(HackSm::DIAG, this->component.m_stateMachine_device3.state); + invoke_to_schedIn(0,0); + dispatchAll(); + ASSERT_EQ(HackSm::OFF, this->component.m_stateMachine_device3.state); + +} + + +// ---------------------------------------------------------------------- +// Helper methods +// ---------------------------------------------------------------------- +void SmTestTester :: + dispatchAll() + { + while (this->component.m_queue.getNumMsgs() > 0) + this->component.doDispatch(); + } + +// ---------------------------------------------------------------------- +// Handlers for typed from ports +// ---------------------------------------------------------------------- + + +} // end namespace FppTest diff --git a/FppTest/state_machine/test/ut/SmTestTester.hpp b/FppTest/state_machine/test/ut/SmTestTester.hpp new file mode 100644 index 0000000000..66021b5622 --- /dev/null +++ b/FppTest/state_machine/test/ut/SmTestTester.hpp @@ -0,0 +1,82 @@ +// ====================================================================== +// \title SmTest/test/ut/Tester.hpp +// \author watney +// \brief hpp file for SmTest test harness implementation class +// ====================================================================== + +#ifndef FppTest_SmTest_Tester_HPP +#define FppTest_SmTest_Tester_HPP + +#include "SmTestGTestBase.hpp" +#include "FppTest/state_machine/SmTest.hpp" +#include "STest/Pick/Pick.hpp" + +namespace FppTest { + +class SmTestTester : public SmTestGTestBase { + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + public: + // Maximum size of histories storing events, telemetry, and port outputs + static constexpr FwSizeType MAX_HISTORY_SIZE = 10; + // Instance ID supplied to the component instance under test + static constexpr FwSizeType TEST_INSTANCE_ID = 0; + // Queue depth supplied to component instance under test + static constexpr FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 10; + // The component id base + static constexpr FwDpIdType ID_BASE = 100; + // The max string length for string data + static constexpr FwSizeType MAX_STRING_LENGTH = 100; + + //! Construct object Tester + //! + SmTestTester(); + + //! Destroy object Tester + //! + ~SmTestTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! schedIn OK + void schedIn_OK(); + + + private: + // ---------------------------------------------------------------------- + // Handlers for data product ports + // ---------------------------------------------------------------------- + + private: + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + //! Connect ports + //! + void connectPorts(); + + //! Initialize components + //! + void initComponents(); + + void dispatchAll(); + + private: + // ---------------------------------------------------------------------- + // Variables + // ---------------------------------------------------------------------- + + + //! The component under test + SmTest component; +}; + +} // end namespace FppTest + +#endif diff --git a/FppTest/typed_tests/StringTest.hpp b/FppTest/typed_tests/StringTest.hpp index db29a7d2da..bf45cd6803 100644 --- a/FppTest/typed_tests/StringTest.hpp +++ b/FppTest/typed_tests/StringTest.hpp @@ -34,7 +34,7 @@ class StringTest : public ::testing::Test { // Truncate fwStr for comparison char fwStrBuf2[bufferSize]; - Fw::StringUtils::string_copy(fwStrBuf2, fwStr.toChar(), sizeof fwStrBuf2); + Fw::StringUtils::string_copy(fwStrBuf2, fwStr.toChar(), static_cast(sizeof fwStrBuf2)); fwSubstr = fwStrBuf2; } diff --git a/Fw/CMakeLists.txt b/Fw/CMakeLists.txt index 86291b9083..8905c8647e 100644 --- a/Fw/CMakeLists.txt +++ b/Fw/CMakeLists.txt @@ -14,6 +14,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Tlm/") # Framework subdirectories add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cfg/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Comp/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Sm/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FilePacket/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Obj/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Port/") diff --git a/Fw/Sm/CMakeLists.txt b/Fw/Sm/CMakeLists.txt new file mode 100644 index 0000000000..2df8067315 --- /dev/null +++ b/Fw/Sm/CMakeLists.txt @@ -0,0 +1,11 @@ +#### +# 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}/SmSignalBuffer.cpp" +) +register_fprime_module() diff --git a/Fw/Sm/README b/Fw/Sm/README new file mode 100644 index 0000000000..c00d7c7621 --- /dev/null +++ b/Fw/Sm/README @@ -0,0 +1 @@ +SmSignalBuffer.hpp(.cpp) - A buffer holding serialized state machine signal data diff --git a/Fw/Sm/SmSignalBuffer.cpp b/Fw/Sm/SmSignalBuffer.cpp new file mode 100644 index 0000000000..66c047b520 --- /dev/null +++ b/Fw/Sm/SmSignalBuffer.cpp @@ -0,0 +1,55 @@ +#include +#include + +namespace Fw { + + SmSignalBuffer::SmSignalBuffer(const U8 *args, Serializable::SizeType size) : m_bufferData{} { + FW_ASSERT(args != nullptr); + FW_ASSERT(size <= sizeof(this->m_bufferData)); + SerializeStatus stat = SerializeBufferBase::setBuff(args,static_cast(size)); + FW_ASSERT(FW_SERIALIZE_OK == stat,static_cast(stat)); + } + + SmSignalBuffer::SmSignalBuffer() : m_bufferData{} { + } + + SmSignalBuffer::~SmSignalBuffer() { + } + + SmSignalBuffer::SmSignalBuffer(const SmSignalBuffer& other) : Fw::SerializeBufferBase(), + m_bufferData{} + { + FW_ASSERT(other.getBuffAddr() != nullptr); + FW_ASSERT(other.getBuffLength() <= sizeof(this->m_bufferData)); + + SerializeStatus stat = SerializeBufferBase::setBuff(other.m_bufferData,other.getBuffLength()); + FW_ASSERT(FW_SERIALIZE_OK == stat,static_cast(stat)); + } + + SmSignalBuffer& SmSignalBuffer::operator=(const SmSignalBuffer& other) { + if(this == &other) { + return *this; + } + + FW_ASSERT(other.getBuffAddr() != nullptr); + FW_ASSERT(other.getBuffLength() <= sizeof(this->m_bufferData)); + + SerializeStatus stat = SerializeBufferBase::setBuff(other.m_bufferData,other.getBuffLength()); + FW_ASSERT(FW_SERIALIZE_OK == stat,static_cast(stat)); + return *this; + } + + Serializable::SizeType SmSignalBuffer::getBuffCapacity() const { + return sizeof(this->m_bufferData); + } + + const U8* SmSignalBuffer::getBuffAddr() const { + return this->m_bufferData; + } + + U8* SmSignalBuffer::getBuffAddr() { + return this->m_bufferData; + } + +} + diff --git a/Fw/Sm/SmSignalBuffer.hpp b/Fw/Sm/SmSignalBuffer.hpp new file mode 100644 index 0000000000..90e7b2d32a --- /dev/null +++ b/Fw/Sm/SmSignalBuffer.hpp @@ -0,0 +1,42 @@ +/* + * SmSignalBuffer.hpp + * + */ + +/* + * Description: + * This object contains the SmSignalBuffer type, used for attaching data to state machine signals + */ +#ifndef FW_SM_SIGNAL_BUFFER_HPP +#define FW_SM_SIGNAL_BUFFER_HPP + +#include +#include + +namespace Fw { + + class SmSignalBuffer : public SerializeBufferBase { + public: + + enum { + SERIALIZED_TYPE_ID = 1010, + SERIALIZED_SIZE = FW_COM_BUFFER_MAX_SIZE + sizeof(FwSizeStoreType) // size of buffer + storage of size word + }; + + SmSignalBuffer(const U8 *args, Serializable::SizeType size); + SmSignalBuffer(); + SmSignalBuffer(const SmSignalBuffer& other); + virtual ~SmSignalBuffer(); + SmSignalBuffer& operator=(const SmSignalBuffer& other); + + Serializable::SizeType getBuffCapacity() const; // !< returns capacity, not current size, of buffer + U8* getBuffAddr(); + const U8* getBuffAddr() const; + + private: + U8 m_bufferData[FW_SM_SIGNAL_BUFFER_MAX_SIZE]; // packet data buffer + }; + +} + +#endif diff --git a/Fw/Types/StringBase.cpp b/Fw/Types/StringBase.cpp index c3ff506b17..3a076e7a65 100644 --- a/Fw/Types/StringBase.cpp +++ b/Fw/Types/StringBase.cpp @@ -24,7 +24,7 @@ StringBase::StringBase() {} StringBase::~StringBase() {} const CHAR* StringBase::operator+=(const CHAR* src) { - this->appendBuff(src, StringUtils::string_length(src, this->getCapacity())); + this->appendBuff(src, static_cast(StringUtils::string_length(src, this->getCapacity()))); return this->toChar(); } diff --git a/Fw/Types/StringBase.hpp b/Fw/Types/StringBase.hpp index a0fddc0cbd..5bd2b22b22 100644 --- a/Fw/Types/StringBase.hpp +++ b/Fw/Types/StringBase.hpp @@ -28,7 +28,7 @@ class StringBase : public Serializable { virtual SizeType getCapacity() const = 0; //!< return size of buffer SizeType length() const; //!< Get length of string - //! Get the maximum length of a string that the buffer can hold + //! Get the maximum length of a string that the buffer can hold (which is capacity - 1) SizeType maxLength() const; //! Get the static serialized size of a string //! This is the max length of the string plus the size of the stored size diff --git a/Fw/Types/StringUtils.cpp b/Fw/Types/StringUtils.cpp index 08e9fbd332..0ca16c265d 100644 --- a/Fw/Types/StringUtils.cpp +++ b/Fw/Types/StringUtils.cpp @@ -3,22 +3,6 @@ #include #include -char* Fw::StringUtils::string_copy(char* destination, const char* source, U32 num) { - // Check for size support - FW_ASSERT(std::numeric_limits::max() <= std::numeric_limits::max()); - char* returned = Fw::StringUtils::string_copy(destination, source, static_cast(num)); - return returned; -} - -U32 Fw::StringUtils::string_length(const CHAR* source, U32 max_len) { - // Check for size support - FW_ASSERT(std::numeric_limits::max() <= std::numeric_limits::max()); - FwSizeType returned = Fw::StringUtils::string_length(source, static_cast(max_len)); - // Range checking for type remapping - FW_ASSERT(returned <= static_cast(std::numeric_limits::max())); - return static_cast(returned); -} - char* Fw::StringUtils::string_copy(char* destination, const char* source, FwSizeType num) { // Handle self-copy and 0 bytes copy if (destination == source || num == 0) { @@ -37,7 +21,7 @@ char* Fw::StringUtils::string_copy(char* destination, const char* source, FwSize } FwSizeType Fw::StringUtils::string_length(const CHAR* source, FwSizeType max_len) { - U32 length = 0; + FwSizeType length = 0; FW_ASSERT(source != nullptr); for (length = 0; length < max_len; length++) { if (source[length] == '\0') { diff --git a/Fw/Types/StringUtils.hpp b/Fw/Types/StringUtils.hpp index be1a46ea37..04b167faac 100644 --- a/Fw/Types/StringUtils.hpp +++ b/Fw/Types/StringUtils.hpp @@ -4,34 +4,6 @@ namespace Fw { namespace StringUtils { - -/** - * \brief copy string with null-termination guaranteed - * - * Standard implementations of strncpy fail to guarantee the termination of a - * string with the null terminator. This implementation guarantees the string is - * properly null-terminated at the possible expense of the last character of the - * copied string being lost. The user is responsible for providing a destination - * large enough for the content and a null-character. Other behavior retains the - * behavior of strncpy. - * - * \param destination: destination buffer to hold copied contents - * \param source: source buffer to read content to copy - * \param num: length of destination buffer - * \return destination buffer - */ -char* string_copy(char* destination, const char* source, U32 num); - -/** - * \brief get the length of the source string or max_len if the string is - * longer than max_len. - * - * \param source: string to calculate the length - * \param max_len: the maximum length of the source string - * \return length of the source string or max_len - */ -U32 string_length(const CHAR* source, U32 max_len); - /** * \brief copy string with null-termination guaranteed * diff --git a/Os/CMakeLists.txt b/Os/CMakeLists.txt index f208c3184f..66c2a01820 100644 --- a/Os/CMakeLists.txt +++ b/Os/CMakeLists.txt @@ -35,9 +35,12 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/ValidatedFile.cpp" # Refactored common files + "${CMAKE_CURRENT_LIST_DIR}/Os.cpp" "${CMAKE_CURRENT_LIST_DIR}/File.cpp" "${CMAKE_CURRENT_LIST_DIR}/Task.cpp" "${CMAKE_CURRENT_LIST_DIR}/Mutex.cpp" + "${CMAKE_CURRENT_LIST_DIR}/FileSystem.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Directory.cpp" ) # Check for default logger if (NOT FPRIME_DISABLE_DEFAULT_LOGGER) @@ -56,8 +59,6 @@ if (FPRIME_USE_POSIX) "${CMAKE_CURRENT_LIST_DIR}/Linux/InterruptLock.cpp" "${CMAKE_CURRENT_LIST_DIR}/Linux/WatchdogTimer.cpp" "${CMAKE_CURRENT_LIST_DIR}/Posix/IntervalTimer.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Linux/FileSystem.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Linux/Directory.cpp" ) endif() # Darwin IPC queue implementation @@ -97,8 +98,11 @@ endif() register_fprime_module() require_fprime_implementation(Os/File) +require_fprime_implementation(Os/FileSystem) +require_fprime_implementation(Os/Directory) +require_fprime_implementation(Os/Mutex) # require_fprime_implementation(Os/Task) # should be added in -require_fprime_implementation(Os/Mutex) +# require_fprime_implementation(Os/Console) # should be added in ### UTS ### Note: 3 separate UTs registered here. @@ -106,17 +110,21 @@ 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/OsSystemResourcesTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsMutexBasicLockableTest.cpp" ) register_fprime_ut() if (BUILD_TESTING) - foreach (TEST IN ITEMS StubFileTest PosixFileTest StubMutexTest PosixMutexTest) # add? : StubTaskTest PosixTaskTest - if (TARGET "${TEST}") - add_dependencies("Os_ut_exe" "${TEST}") - endif() + foreach (TEST IN ITEMS + StubFileTest PosixFileTest + StubMutexTest PosixMutexTest + StubDirectoryTest PosixDirectory + StubFileSystemTest PosixFileSystemTest + ) # add? : StubTaskTest PosixTaskTest + if (TARGET "${TEST}") + add_dependencies("Os_ut_exe" "${TEST}") + endif() endforeach() endif() # Second UT Pthreads diff --git a/Os/Console.cpp b/Os/Console.cpp index 5cdd52bde3..56113d42b0 100644 --- a/Os/Console.cpp +++ b/Os/Console.cpp @@ -58,12 +58,9 @@ namespace Os { } Console& Console::getSingleton() { - // On the fly construction of singleton - if (s_singleton == nullptr) { - s_singleton = new Console(); - Fw::Logger::registerLogger(s_singleton); - } - return *s_singleton; + static Console s_singleton; + Fw::Logger::registerLogger(&s_singleton); + return s_singleton; } } diff --git a/Os/Directory.cpp b/Os/Directory.cpp new file mode 100644 index 0000000000..9424d8629b --- /dev/null +++ b/Os/Directory.cpp @@ -0,0 +1,148 @@ +// ====================================================================== +// \title Os/Directory.cpp +// \brief common function implementation for Os::Directory +// ====================================================================== +#include +#include + +namespace Os { + +Directory::Directory() : m_is_open(false), m_handle_storage(), m_delegate(*DirectoryInterface::getDelegate(m_handle_storage)) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); +} + +Directory::~Directory() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + if (this->m_is_open) { + this->close(); + } + this->m_delegate.~DirectoryInterface(); +} + +// ------------------------------------------------------------ +// Directory operations delegating to implementation +// ------------------------------------------------------------ +DirectoryHandle* Directory::getHandle() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + return this->m_delegate.getHandle(); +} + +Directory::Status Directory::open(const char* path, OpenMode mode) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + FW_ASSERT(mode >= 0 and mode < OpenMode::MAX_OPEN_MODE); + Status status = this->m_delegate.open(path, mode); + if (status == Status::OP_OK) { + this->m_is_open = true; + } + return status; +} + +bool Directory::isOpen() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + return this->m_is_open; +} +Directory::Status Directory::rewind() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + if (not this->m_is_open) { + return Status::NOT_OPENED; + } + return this->m_delegate.rewind(); +} + +Directory::Status Directory::read(char * fileNameBuffer, FwSizeType bufSize) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + if (not this->m_is_open) { + return Status::NOT_OPENED; + } + FW_ASSERT(fileNameBuffer != nullptr); + Status status = this->m_delegate.read(fileNameBuffer, bufSize); + fileNameBuffer[bufSize - 1] = '\0'; // Guarantee null-termination + return status; +} + +Directory::Status Directory::read(Fw::StringBase& filename) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + if (not this->m_is_open) { + return Status::NOT_OPENED; + } + return this->m_delegate.read(const_cast(filename.toChar()), filename.getCapacity()); +} + +void Directory::close() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + this->m_is_open = false; + return this->m_delegate.close(); +} + +// ------------------------------------------------------------ +// Common functions built on top of OS-specific functions +// ------------------------------------------------------------ + +Directory::Status Directory::getFileCount(FwSizeType& fileCount) { + if (not this->m_is_open) { + return Status::NOT_OPENED; + } + // Rewind to ensure we start from the beginning of the stream + if (this->rewind() != Status::OP_OK) { + return Status::OTHER_ERROR; + } + const FwSizeType loopLimit = std::numeric_limits::max(); + FwSizeType count = 0; + char unusedBuffer[1]; // buffer must have size but is unused + Status readStatus = Status::OP_OK; + fileCount = 0; + // Count files by reading each file entry until there is NO_MORE_FILES + for (FwSizeType iter = 0; iter < loopLimit; ++iter) { + readStatus = this->read(unusedBuffer, sizeof(unusedBuffer)); + if (readStatus == Status::NO_MORE_FILES) { + break; + } else if (readStatus != Status::OP_OK) { + return Status::OTHER_ERROR; + } + count++; + } + fileCount = count; + if (this->rewind() != Status::OP_OK) { + return Status::OTHER_ERROR; + } + return Status::OP_OK; +} + + +Directory::Status Directory::readDirectory(Fw::String filenameArray[], const FwSizeType filenameArraySize, FwSizeType& filenameCount) { + FW_ASSERT(filenameArray != nullptr); + FW_ASSERT(filenameArraySize > 0); + if (not this->m_is_open) { + return Status::NOT_OPENED; + } + // Rewind to ensure we start reading from the beginning of the stream + if (this->rewind() != Status::OP_OK) { + return Status::OTHER_ERROR; + } + + Status readStatus = Status::OP_OK; + Status returnStatus = Status::OP_OK; + FwSizeType index; + filenameCount = 0; + // Iterate through the directory and read the filenames into the array + for (index = 0; index < filenameArraySize; index++) { + readStatus = this->read(filenameArray[index]); + if (readStatus == Status::NO_MORE_FILES) { + break; + } else if (readStatus != Status::OP_OK) { + return Status::OTHER_ERROR; + } + } + filenameCount = index; + + if (this->rewind() != Status::OP_OK) { + return Status::OTHER_ERROR; + } + + return returnStatus; + +} + + +} // namespace Os diff --git a/Os/Directory.hpp b/Os/Directory.hpp index 2aec3cf1ce..c5a1b6f308 100644 --- a/Os/Directory.hpp +++ b/Os/Directory.hpp @@ -1,44 +1,225 @@ -#ifndef _Directory_hpp_ -#define _Directory_hpp_ +// ====================================================================== +// \title Os/Directory.hpp +// \brief Os::Directory interface definition +// ====================================================================== + +#ifndef _OS_DIRECTORY_HPP_ +#define _OS_DIRECTORY_HPP_ #include +#include +#include namespace Os { - // This class encapsulates a very simple directory interface that has the most often-used features +struct DirectoryHandle {}; - class Directory { - public: +class DirectoryInterface { + public: + enum Status { + OP_OK, //!< Operation was successful + DOESNT_EXIST, //!< Directory doesn't exist + NO_PERMISSION, //!< No permission to read directory + NOT_OPENED, //!< Directory hasn't been opened yet + NOT_DIR, //!< Path is not a directory + NO_MORE_FILES, //!< Directory stream has no more files + FILE_LIMIT, //!< Directory has more files than can be read + BAD_DESCRIPTOR, //!< Directory stream descriptor is invalid + ALREADY_EXISTS, //!< Directory already exists + NOT_SUPPORTED, //!< Operation is not supported by the current implementation + OTHER_ERROR, //!< A catch-all for other errors. Have to look in implementation-specific code + }; - typedef enum { - OP_OK, //!< Operation was successful - DOESNT_EXIST, //!< Directory doesn't exist - NO_PERMISSION, //!< No permission to read directory - NOT_OPENED, //!< Directory hasn't been opened yet - NOT_DIR, //!< Path is not a directory - NO_MORE_FILES, //!< Directory stream has no more files - OTHER_ERROR, //!< A catch-all for other errors. Have to look in implementation-specific code - } Status; + enum OpenMode { + READ, //!< Error if directory doesn't exist + CREATE_IF_MISSING, //!< Create directory if it doesn't exist + CREATE_EXCLUSIVE, //!< Create directory and error if it already exists + MAX_OPEN_MODE //!< Maximum value of OpenMode + }; - Directory(); //!< Constructor - virtual ~Directory(); //!< Destructor. Will close directory if still open - Status open(const char* dirName); //!< open directory. Directory must already exist - bool isOpen(); //!< check if file descriptor is open or not. - Status rewind(); //!< rewind directory stream to the beginning + //! \brief default constructor + DirectoryInterface() = default; - Status read(char * fileNameBuffer, U32 bufSize); //!< get next filename from directory - Status read(char * fileNameBuffer, U32 bufSize, I64& inode); //!< get next filename and inode from directory - void close(); //!< close directory + //! \brief default virtual destructor + virtual ~DirectoryInterface() = default; - NATIVE_INT_TYPE getLastError(); //!< read back last error code (typically errno) - const char* getLastErrorString(); //!< get a string of the last error (typically from strerror) + //! \brief copy constructor is forbidden + DirectoryInterface(const DirectoryInterface& other) = delete; - private: + //! \brief assignment operator is forbidden + DirectoryInterface& operator=(const DirectoryInterface& other) = delete; - POINTER_CAST m_dir; //!< Stored directory pointer (type varies across implementations) - NATIVE_INT_TYPE m_lastError; //!< stores last error + //! \brief return the underlying Directory handle (implementation specific) + //! \return internal Directory handle representation + virtual DirectoryHandle* getHandle() = 0; - }; + //! \brief provide a pointer to a Directory delegate object + static DirectoryInterface* getDelegate(HandleStorage& aligned_new_memory); + + + // ----------------------------------------------------------------- + // Directory operations to be implemented by an OSAL implementation + // ----------------------------------------------------------------- + // These functions are to be overridden in each OS implementation + // See an example in in Os/Posix/Directory.hpp + + //! \brief Open or create a directory + //! + //! Using the path provided, this function will open or create a directory. + //! Use OpenMode::READ to open an existing directory and error if the directory is not found + //! Use OpenMode::CREATE_IF_MISSING to open a directory, creating the directory if it doesn't exist + //! Use OpenMode::CREATE_EXCLUSIVE to open a directory, creating the directory and erroring if it already exists + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! + //! \param path: path of directory to open + //! \param mode: enum (READ, CREATE_IF_MISSING, CREATE_EXCLUSIVE). See notes above for more information + //! \return status of the operation + virtual Status open(const char* path, OpenMode mode) = 0; + + //! \brief Rewind directory stream + //! + //! Each read operation moves the seek position forward. This function resets the seek position to the beginning. + //! + //! \return status of the operation + virtual Status rewind() = 0; + + //! \brief Get next filename from directory stream + //! + //! Write at most buffSize characters of the file name to fileNameBuffer and guarantee null-termination. + //! This function skips the current directory (.) and parent directory (..) entries. + //! Returns NO_MORE_FILES if there are no more files to read from the buffer. + //! + //! It is invalid to pass `nullptr` as fileNameBuffer. + //! + //! \param fileNameBuffer: buffer to store filename + //! \param buffSize: size of fileNameBuffer + //! \return status of the operation + virtual Status read(char * fileNameBuffer, FwSizeType buffSize) = 0; + + //! \brief Get next filename from directory stream and write it to a Fw::StringBase object + //! + //! \param filename: Fw::StringBase (or derived) object to store filename in + //! \return status of the operation + // virtual Status read(Fw::StringBase& filename) = 0; + + //! \brief Close directory + virtual void close() = 0; + + +}; + +//! \brief Directory class +//! +//! This class provides a common interface for directory operations, such as reading files in a directory +//! and getting the number of files in a directory. +class Directory final : public DirectoryInterface { + public: + //! \brief Constructor + Directory(); + + //! \brief Destructor + //! + //! Destructor will close the Directory if it is open + ~Directory() final; + + //! \brief return the underlying Directory handle (implementation specific) + //! \return internal Directory handle representation + DirectoryHandle* getHandle() override; + + + // ------------------------------------------------------------ + // Implementation-specific Directory member functions + // ------------------------------------------------------------ + // These functions are overridden in each OS implementation (e.g. in Os/Posix/Directory.hpp) + + //! \brief Open or create a directory + //! + //! Using the path provided, this function will open or create a directory. + //! Use OpenMode::READ to open an existing directory and error if the directory is not found + //! Use OpenMode::CREATE_IF_MISSING to open a directory, creating the directory if it doesn't exist + //! Use OpenMode::CREATE_EXCLUSIVE to open a directory, creating the directory and erroring if it already exists + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! + //! \param path: path of directory to open + //! \param mode: enum (READ, CREATE_IF_MISSING, CREATE_EXCLUSIVE). See notes above for more information + //! \return status of the operation + Status open(const char* path, OpenMode mode) override; + + //! \brief Check if Directory is open or not + //! \return true if Directory is open, false otherwise + bool isOpen(); + + //! \brief Rewind directory stream + //! + //! Each read operation moves the seek position forward. This function resets the seek position to the beginning. + //! + //! \return status of the operation + Status rewind() override; + + //! \brief Get next filename from directory stream + //! + //! Write at most buffSize characters of the file name to fileNameBuffer and guarantee null-termination. + //! This function skips the current directory (.) and parent directory (..) entries. + //! Returns NO_MORE_FILES if there are no more files to read from the buffer. + //! + //! It is invalid to pass `nullptr` as fileNameBuffer. + //! + //! \param fileNameBuffer: buffer to store filename + //! \param buffSize: size of fileNameBuffer + //! \return status of the operation + Status read(char * fileNameBuffer, FwSizeType buffSize) override; + + + //! \brief Close directory + void close() override; + + + // ------------------------------------------------------------ + // Common functions built on top of OS-specific functions + // ------------------------------------------------------------ + + //! \brief Get next filename from directory stream and write it to a Fw::StringBase object + //! + //! \param filename: Fw::StringBase (or derived) object to store filename in + //! \return status of the operation + Status read(Fw::StringBase& filename); + + //! \brief Read the contents of the directory and store filenames in filenameArray of size arraySize. + //! + //! The function first rewinds the directory stream to ensure reading starts from the beginning. + //! After reading, it rewinds the directory stream again, resetting seek position to beginning. + //! + //! \param filenameArray: array to store filenames + //! \param arraySize: size of filenameArray + //! \param filenameCount: number of filenames written to filenameArray (output) + //! \return status of the operation + Status readDirectory(Fw::String filenameArray[], const FwSizeType arraySize, FwSizeType& filenameCount); + + //! \brief Get the number of files in the directory. + //! + //! Counts the number of files in the directory by reading each file entry and writing the count to fileCount. + //! + //! The function first rewinds the directory stream to ensure counting starts from the beginning. + //! After counting, it rewinds the directory stream again, resetting seek position to beginning. + //! + //! \param fileCount Reference to a variable where the file count will be stored. + //! \return Status indicating the result of the operation. + Status getFileCount(FwSizeType& fileCount); + + private: + bool m_is_open; //!< Flag indicating if the directory has been open + + private: + // This section is used to store the implementation-defined Directory handle. To Os::Directory 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) DirectoryHandleStorage m_handle_storage; //!< Directory handle storage + DirectoryInterface& m_delegate; +}; } diff --git a/Os/File.cpp b/Os/File.cpp index 4d8c1aab8a..26e3a27f73 100644 --- a/Os/File.cpp +++ b/Os/File.cpp @@ -60,9 +60,10 @@ File::Status File::open(const CHAR* filepath, File::Mode requested_mode, File::O 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; } - // Reset any open CRC calculations - this->m_crc = File::INITIAL_CRC; + return status; } diff --git a/Os/File.hpp b/Os/File.hpp index 64103b47ee..b189446d0f 100644 --- a/Os/File.hpp +++ b/Os/File.hpp @@ -218,6 +218,7 @@ namespace Os { File(); //! \brief destructor //! + //! Destructor closes the file if it is open ~File() final; //! \brief copy constructor that copies the internal representation diff --git a/Os/FileSystem.cpp b/Os/FileSystem.cpp new file mode 100644 index 0000000000..c2b7a2abfe --- /dev/null +++ b/Os/FileSystem.cpp @@ -0,0 +1,314 @@ +// ====================================================================== +// \title Os/FileSystem.cpp +// \brief common function implementation for Os::FileSystem +// ====================================================================== +#include +#include + +namespace Os { +FileSystem* FileSystem::s_singleton; + + +FileSystem::FileSystem() : m_handle_storage(), m_delegate(*FileSystemInterface::getDelegate(m_handle_storage)) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); +} + +FileSystem::~FileSystem() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + m_delegate.~FileSystemInterface(); +} + +FileSystemHandle* FileSystem::getHandle() { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + return this->m_delegate.getHandle(); +} + +FileSystem::Status FileSystem::_removeDirectory(const char* path) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + return this->m_delegate._removeDirectory(path); +} + +FileSystem::Status FileSystem::_removeFile(const char* path) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + return this->m_delegate._removeFile(path); +} + +FileSystem::Status FileSystem::_rename(const char* sourcePath, const char* destPath) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(sourcePath != nullptr); + FW_ASSERT(destPath != nullptr); + return this->m_delegate._rename(sourcePath, destPath); +} + +FileSystem::Status FileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + FW_ASSERT(bufferSize > 0); // because bufferSize=0 would trigger a malloc in some implementations (e.g. Posix) + return this->m_delegate._getWorkingDirectory(path, bufferSize); +} + +FileSystem::Status FileSystem::_changeWorkingDirectory(const char* path) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + return this->m_delegate._changeWorkingDirectory(path); +} + +FileSystem::Status FileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { + FW_ASSERT(&this->m_delegate == reinterpret_cast(&this->m_handle_storage[0])); + FW_ASSERT(path != nullptr); + return this->m_delegate._getFreeSpace(path, totalBytes, freeBytes); +} + +void FileSystem::init() { + // Force trigger on the fly singleton setup + (void) FileSystem::getSingleton(); +} + +FileSystem& FileSystem::getSingleton() { + static FileSystem s_singleton; + return s_singleton; +} + + +// ------------------------------------------------------------ +// Static functions calling implementation-specific operations +// ------------------------------------------------------------ + +FileSystem::Status FileSystem::removeDirectory(const char* path) { + return FileSystem::getSingleton()._removeDirectory(path); +} + +FileSystem::Status FileSystem::removeFile(const char* path) { + return FileSystem::getSingleton()._removeFile(path); +} + +FileSystem::Status FileSystem::rename(const char* sourcePath, const char* destPath) { + return FileSystem::getSingleton()._rename(sourcePath, destPath); +} + +FileSystem::Status FileSystem::getWorkingDirectory(char* path, FwSizeType bufferSize) { + return FileSystem::getSingleton()._getWorkingDirectory(path, bufferSize); +} + +FileSystem::Status FileSystem::changeWorkingDirectory(const char* path) { + return FileSystem::getSingleton()._changeWorkingDirectory(path); +} + +FileSystem::Status FileSystem::getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { + return FileSystem::getSingleton()._getFreeSpace(path, totalBytes, freeBytes); +} + + +// ------------------------------------------------------------ +// Additional functions built on top of OS-specific operations +// ------------------------------------------------------------ + +FileSystem::Status FileSystem::createDirectory(const char* path, bool errorIfAlreadyExists) { + FW_ASSERT(path != nullptr); + Status status = Status::OP_OK; + Os::Directory dir; + // If errorIfAlreadyExists is true, use CREATE_EXCLUSIVE mode, otherwise use CREATE_IF_MISSING + Directory::OpenMode mode = errorIfAlreadyExists ? Directory::OpenMode::CREATE_EXCLUSIVE : Directory::OpenMode::CREATE_IF_MISSING; + Directory::Status dirStatus = dir.open(path, mode); + dir.close(); + if (dirStatus != Directory::OP_OK) { + return FileSystem::handleDirectoryError(dirStatus); + } + return status; +} + +FileSystem::Status FileSystem::touch(const char* path) { + FW_ASSERT(path != nullptr); + Status status = Status::OP_OK; + Os::File file; + File::Status file_status = file.open(path, Os::File::OPEN_WRITE); + file.close(); + if (file_status != File::OP_OK) { + status = FileSystem::handleFileError(file_status); + } + return status; +} + +FileSystem::PathType FileSystem::getPathType(const char* path) { + FW_ASSERT(path != nullptr); + Os::File file; + File::Status file_status = file.open(path, Os::File::OPEN_READ); + file.close(); + if (file_status == File::OP_OK) { + return PathType::FILE; + } + Os::Directory dir; + Directory::Status dir_status = dir.open(path, Os::Directory::OpenMode::READ); + dir.close(); + if (dir_status == Directory::Status::OP_OK) { + return PathType::DIRECTORY; + } + return PathType::NOT_EXIST; +} // end getPathType + +bool FileSystem::exists(const char* path) { + return FileSystem::getPathType(path) != PathType::NOT_EXIST; +} // end exists + +FileSystem::Status FileSystem::copyFile(const char* sourcePath, const char* destPath) { + FW_ASSERT(sourcePath != nullptr); + FW_ASSERT(destPath != nullptr); + Os::File source; + Os::File destination; + Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ); + if (fileStatus != Os::File::OP_OK) { + return FileSystem::handleFileError(fileStatus); + } + fileStatus = destination.open(destPath, Os::File::OPEN_WRITE); + if (fileStatus != Os::File::OP_OK) { + return FileSystem::handleFileError(fileStatus); + } + + FwSignedSizeType sourceFileSize = 0; + FileSystem::Status fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize); + if (fs_status != FileSystem::Status::OP_OK) { + return fs_status; + } + + fs_status = FileSystem::copyFileData(source, destination, sourceFileSize); + + return fs_status; +} // end copyFile + +FileSystem::Status FileSystem::appendFile(const char* sourcePath, const char* destPath, bool createMissingDest) { + Os::File source; + Os::File destination; + + // If requested, check if destination file exists and exit if does not exist + if (not createMissingDest and not FileSystem::exists(destPath)) { + return Status::DOESNT_EXIST; + } + + Os::File::Status fileStatus = source.open(sourcePath, Os::File::OPEN_READ); + if (fileStatus != Os::File::OP_OK) { + return FileSystem::handleFileError(fileStatus); + } + fileStatus = destination.open(destPath, Os::File::OPEN_APPEND); + if (fileStatus != Os::File::OP_OK) { + return FileSystem::handleFileError(fileStatus); + } + + FileSystem::Status fs_status = FileSystem::OP_OK; + + FwSignedSizeType sourceFileSize = 0; + fs_status = FileSystem::getFileSize(sourcePath, sourceFileSize); + if (fs_status != FileSystem::Status::OP_OK) { + return fs_status; + } + + fs_status = FileSystem::copyFileData(source, destination, sourceFileSize); + + return fs_status; +} // end appendFile + +FileSystem::Status FileSystem::moveFile(const char* source, const char* destination) { + Status status = Status::OP_OK; + + // Try to rename the file + status = FileSystem::rename(source, destination); + + // If rename fails because of cross-device rename, attempt to copy and remove instead + if (status == Status::EXDEV_ERROR) { + status = FileSystem::copyFile(source, destination); + if (status != Status::OP_OK) { + return status; + } + status = FileSystem::removeFile(source); + } + + return status; +} + +FileSystem::Status FileSystem::getFileSize(const char* path, FwSignedSizeType& size) { + Os::File file; + Os::File::Status status = file.open(path, Os::File::OPEN_READ); + if (status != File::Status::OP_OK) { + return FileSystem::handleFileError(status); + } + status = file.size(size); + if (status != File::Status::OP_OK) { + return FileSystem::handleFileError(status); + } + return FileSystem::OP_OK; +} + + +// ------------------------------------------------------------ +// Internal helper functions +// ------------------------------------------------------------ + +FileSystem::Status FileSystem::handleFileError(File::Status fileStatus) { + FileSystem::Status status = FileSystem::OTHER_ERROR; + + switch (fileStatus) { + case File::NO_SPACE: + status = FileSystem::NO_SPACE; + break; + case File::NO_PERMISSION: + status = FileSystem::NO_PERMISSION; + break; + case File::DOESNT_EXIST: + status = FileSystem::DOESNT_EXIST; + break; + default: + status = FileSystem::OTHER_ERROR; + } + return status; +} // end handleFileError + +FileSystem::Status FileSystem::handleDirectoryError(Directory::Status dirStatus) { + FileSystem::Status status = FileSystem::OTHER_ERROR; + + switch (dirStatus) { + case Directory::DOESNT_EXIST: + status = FileSystem::DOESNT_EXIST; + break; + case Directory::NO_PERMISSION: + status = FileSystem::NO_PERMISSION; + break; + case Directory::ALREADY_EXISTS: + status = FileSystem::ALREADY_EXISTS; + break; + case Directory::NOT_SUPPORTED: + status = FileSystem::NOT_SUPPORTED; + break; + default: + status = FileSystem::OTHER_ERROR; + } + return status; +} // end handleFileError + +FileSystem::Status FileSystem::copyFileData(File& source, File& destination, FwSignedSizeType size) { + static_assert(FILE_SYSTEM_FILE_CHUNK_SIZE != 0, "FILE_SYSTEM_FILE_CHUNK_SIZE must be >0"); + U8 fileBuffer[FILE_SYSTEM_FILE_CHUNK_SIZE]; + File::Status file_status; + + FwSignedSizeType copiedSize = 0; + FwSignedSizeType chunkSize = FILE_SYSTEM_FILE_CHUNK_SIZE; + + // Copy the file in chunks - loop until all data is copied + for (copiedSize = 0; copiedSize < size; copiedSize += chunkSize) { + // chunkSize is FILE_SYSTEM_FILE_CHUNK_SIZE unless size-copiedSize is less than that + // in which case chunkSize is size-copiedSize, ensuring the last chunk reads the remaining data + chunkSize = FW_MIN(FILE_SYSTEM_FILE_CHUNK_SIZE, size - copiedSize); + file_status = source.read(fileBuffer, chunkSize, Os::File::WaitType::WAIT); + if (file_status != File::OP_OK) { + return FileSystem::handleFileError(file_status); + } + file_status = destination.write(fileBuffer, chunkSize, Os::File::WaitType::WAIT); + if (file_status != File::OP_OK) { + return FileSystem::handleFileError(file_status); + } + } + + return FileSystem::OP_OK; +} // end copyFileData + +} // namespace Os diff --git a/Os/FileSystem.hpp b/Os/FileSystem.hpp index 00ce1a3575..a738e552ef 100644 --- a/Os/FileSystem.hpp +++ b/Os/FileSystem.hpp @@ -1,42 +1,390 @@ -#ifndef _FileSystem_hpp_ -#define _FileSystem_hpp_ +// ====================================================================== +// \title Os/FileSystem.hpp +// \brief Os::FileSystem interface definition +// ====================================================================== -#include -#include +#ifndef _OS_FILESYSTEM_HPP_ +#define _OS_FILESYSTEM_HPP_ -#define FILE_SYSTEM_CHUNK_SIZE (256u) +#include +#include +#include +#include namespace Os { - // This namespace encapsulates a very simple file system interface that has the most often-used features. - namespace FileSystem { - - typedef enum { - OP_OK, //!< Operation was successful - ALREADY_EXISTS, //!< File already exists - NO_SPACE, //!< No space left - NO_PERMISSION, //!< No permission to write - NOT_DIR, //!< Path is not a directory - IS_DIR, //!< Path is a directory - NOT_EMPTY, //!< directory is not empty - INVALID_PATH, //!< Path is too long, too many sym links, doesn't exist, ect - FILE_LIMIT, //!< Too many files or links - BUSY, //!< Operand is in use by the system or by a process - OTHER_ERROR, //!< other OS-specific error - } Status; - - Status createDirectory(const char* path); //!< create a new directory at location path - Status removeDirectory(const char* path); //!< remove a directory at location path - Status readDirectory(const char* path, const U32 maxNum, Fw::String fileArray[], U32& numFiles); //!< read the contents of a directory. Size of fileArray should be maxNum. Cleaner implementation found in Directory.hpp - Status removeFile(const char* path); //!< removes a file at location path - 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, 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 - } +struct FileSystemHandle {}; + +class FileSystemInterface { + public: + + // Size of file chunks to use for file system operations (e.g. copyFile) + static constexpr FwSignedSizeType FILE_SYSTEM_FILE_CHUNK_SIZE = FW_FILE_CHUNK_SIZE; //!< Size of file system chunk + + enum Status { + OP_OK, //!< Operation was successful + ALREADY_EXISTS, //!< File already exists + NO_SPACE, //!< No space left + NO_PERMISSION, //!< No permission to write + NOT_DIR, //!< Path is not a directory + IS_DIR, //!< Path is a directory + NOT_EMPTY, //!< directory is not empty + INVALID_PATH, //!< Path is too long, too many sym links, etc. + DOESNT_EXIST, //!< Path doesn't exist + FILE_LIMIT, //!< Too many files or links + BUSY, //!< Operand is in use by the system or by a process + NO_MORE_FILES, //!< Directory stream has no more files + BUFFER_TOO_SMALL, //!< Buffer size is too small to hold full path (for getWorkingDirectory) + EXDEV_ERROR, // Operation not supported across devices (e.g. rename) + OVERFLOW_ERROR, // Operation failed due to overflow in calculation of the result + NOT_SUPPORTED, //!< Operation is not supported by the current implementation + OTHER_ERROR, //!< other OS-specific error + }; + + enum PathType { + FILE, //!< Path is a file + DIRECTORY, //!< Path is a directory + NOT_EXIST, //!< Path does not exist + }; + + //! \brief default constructor + FileSystemInterface() = default; + + //! \brief default virtual destructor + virtual ~FileSystemInterface() = default; + + //! \brief copy constructor is forbidden + FileSystemInterface(const FileSystemInterface& other) = delete; + + //! \brief assignment operator is forbidden + FileSystemInterface& operator=(const FileSystemInterface& other) = delete; + + //! \brief return the underlying FileSystem handle (implementation specific) + //! \return internal FileSystem handle representation + virtual FileSystemHandle* getHandle() = 0; + + //! \brief provide a pointer to a FileSystem delegate object + static FileSystemInterface* getDelegate(HandleStorage& aligned_new_memory); + + + // ------------------------------------------------------------------ + // FileSystem operations to be implemented by an OSAL implementation + // ------------------------------------------------------------------ + // These functions are to be overridden in each OS implementation + // See an example in in Os/Posix/FileSystem.hpp + + //! \brief Remove a directory at the specified path + //! \param path The path of the directory to remove + //! \return Status of the operation + virtual Status _removeDirectory(const char* path) = 0; + + //! \brief Remove a file at the specified path + //! \param path The path of the file to remove + //! \return Status of the operation + virtual Status _removeFile(const char* path) = 0; + + //! \brief Rename (or move) a file from source to destination + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + virtual Status _rename(const char* sourcePath, const char* destPath) = 0; + + //! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path + //! \param path The path on the filesystem to query + //! \param totalBytes Reference to store the total bytes on the filesystem + //! \param freeBytes Reference to store the free bytes on the filesystem + //! \return Status of the operation + virtual Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) = 0; + + //! \brief Get the current working directory + //! \param path Buffer to store the current working directory path + //! \param bufferSize Size of the buffer + //! \return Status of the operation + virtual Status _getWorkingDirectory(char* path, FwSizeType bufferSize) = 0; + + //! \brief Change the current working directory to the specified path + //! \param path The path of the new working directory + //! \return Status of the operation + virtual Status _changeWorkingDirectory(const char* path) = 0; + +}; + +//! \brief FileSystem class +//! +//! This class provides a common interface for file system operations. +//! This class uses the singleton pattern and should be accessed through +//! its static functions, for example using `Os::FileSystem::removeFile(path)`. +class FileSystem final : public FileSystemInterface { + private: + FileSystem(); //!< Constructor (private because singleton pattern) + public: + ~FileSystem() final; //!< Destructor + + //! \brief return the underlying FileSystem handle (implementation specific) + //! \return internal FileSystem handle representation + FileSystemHandle* getHandle() override; + + + // ------------------------------------------------------------ + // Implementation-specific FileSystem member functions + // ------------------------------------------------------------ + + //! \brief Remove a directory at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the directory to remove + //! \return Status of the operation + Status _removeDirectory(const char* path) override; + + //! \brief Remove a file at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file to remove + //! \return Status of the operation + Status _removeFile(const char* path) override; + + //! \brief Rename a file from source to destination + //! + //! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR + //! and moveFile can be used instead to force a copy-and-remove. + //! + //! It is invalid to pass `nullptr` as sourcePath or destPath. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + Status _rename(const char* sourcePath, const char* destPath) override; + + //! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path on the filesystem to query + //! \param totalBytes Reference to store the total bytes on the filesystem + //! \param freeBytes Reference to store the free bytes on the filesystem + //! \return Status of the operation + Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) override; + + //! \brief Get the current working directory + //! + //! Writes the current working directory path to the provided buffer of size bufferSize. + //! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL. + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to pass a bufferSize of 0. + //! + //! \param path Buffer to store the current working directory path + //! \param bufferSize Size of the buffer + //! \return Status of the operation + Status _getWorkingDirectory(char* path, FwSizeType bufferSize) override; + + //! \brief Change the current working directory to the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the new working directory + //! \return Status of the operation + Status _changeWorkingDirectory(const char* path) override; + + + // ------------------------------------------------------------ + // Implementation-specific FileSystem static functions + // ------------------------------------------------------------ + // These are static variants that are exposed to the user, and call the above member functions + + //! \brief Remove a directory at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the directory to remove + //! \return Status of the operation + static Status removeDirectory(const char* path); + + //! \brief Remove a file at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file to remove + //! \return Status of the operation + static Status removeFile(const char* path); + + //! \brief Rename a file from source to destination + //! + //! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR + //! and moveFile can be used instead to force a copy-and-remove. + //! + //! It is invalid to pass `nullptr` as sourcePath or destPath. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + static Status rename(const char* sourcePath, const char* destPath); + + //! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path on the filesystem to query + //! \param totalBytes Reference to store the total bytes on the filesystem + //! \param freeBytes Reference to store the free bytes on the filesystem + //! \return Status of the operation + static Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes); + + //! \brief Get the current working directory + //! + //! Writes the current working directory path to the provided buffer of size bufferSize. + //! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL. + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to pass a bufferSize of 0. + //! + //! \param path Buffer to store the current working directory path + //! \param bufferSize Size of the buffer + //! \return Status of the operation + static Status getWorkingDirectory(char* path, FwSizeType bufferSize); + + //! \brief Change the current working directory to the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the new working directory + //! \return Status of the operation + static Status changeWorkingDirectory(const char* path); + + + // ------------------------------------------------------------ + // Additional functions built on top of OS-specific operations + // ------------------------------------------------------------ + + //! \brief Return true if the path exists, false otherwise + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path to check for existence + //! \return True if the path exists, false otherwise + static bool exists(const char* path); + + //! \brief Return the type of the path (file, directory, or doesn't exist) + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path to check for existence + //! \return PathType enum representing the type of the path (FILE, DIRECTORY, NOT_EXIST) + static PathType getPathType(const char* path); + + //! \brief Touch a file at the specified path, creating it if it doesn't exist + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file to touch + //! \return Status of the operation + static Status touch(const char* path); + + //! \brief Create a new directory at the specified path. + //! + //! The optional errorIfAlreadyExists (default=false) parameter can be set to true + //! to return an error status if the directory already exists. + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path where the new directory will be created + //! \param errorIfAlreadyExists If true, returns an error if the directory already exists + //! \return Status of the operation + static Status createDirectory(const char* path, bool errorIfAlreadyExists=false); + + //! \brief Append the source file to the destination file + //! + //! This function opens both files, and iteratively reads the source by chunks and writes + //! chunks to the destination. + //! If the destination file does not exist and createMissingDest is true, a new file is created. + //! + //! It is invalid to pass `nullptr` as either the source or destination path. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \param createMissingDest If true, creates a new file if the destination doesn't exist + //! \return Status of the operation + static Status appendFile(const char* sourcePath, const char* destPath, bool createMissingDest=false); + + //! \brief Copy a file from the source path to the destination path + //! + //! This function opens both files, and iteratively reads the source by chunks and writes + //! chunks to the destination. + //! + //! It is invalid to pass `nullptr` as either the source or destination path. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + static Status copyFile(const char* sourcePath, const char* destPath); + + //! \brief Move a file from sourcePath to destPath + //! + //! This is done by first trying to rename, and if renaming fails, + //! copy it and then remove the original + //! + //! It is invalid to pass `nullptr` as either the source or destination path. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + static Status moveFile(const char* sourcePath, const char* destPath); + + //! \brief Get the size of the file (in bytes) at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file + //! \param size Reference to store the size of the file + //! \return Status of the operation + static Status getFileSize(const char* path, FwSignedSizeType& size); + + + public: + //! \brief initialize singleton + static void init(); + + //! \brief get a reference to singleton + //! \return reference to singleton + static FileSystem& getSingleton(); + + private: + // ------------------------------------------------------------ + // Internal helper functions + // ------------------------------------------------------------ + + //! \brief Convert a File::Status to a FileSystem::Status + static Status handleFileError(File::Status fileStatus); + + //! \brief Convert a Directory::Status to a FileSystem::Status + static Status handleDirectoryError(Directory::Status dirStatus); + + //! \brief A helper function that writes all the file information in the source + //! file to the destination file (replaces/appends to end/etc. depending + //! on destination file mode). + //! + //! Files must already be open and will remain open after this function + //! completes. + //! + //! @param source File to copy data from + //! @param destination File to copy data to + //! @param size The number of bytes to copy + static Status copyFileData(File& source, File& destination, FwSignedSizeType size); + + private: + // This section is used to store the implementation-defined FileSystem handle. To Os::FileSystem 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) FileSystemHandleStorage m_handle_storage; //!< FileSystem handle storage + FileSystemInterface& m_delegate; + + static FileSystem* s_singleton; +}; + + } diff --git a/Os/Linux/Directory.cpp b/Os/Linux/Directory.cpp deleted file mode 100644 index d1f5b832cf..0000000000 --- a/Os/Linux/Directory.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -namespace Os { - - Directory::Directory() :m_dir(0),m_lastError(0) { - } - - Directory::~Directory() { - this->close(); - } - - Directory::Status Directory::open(const char* dirName) { - - Status stat = OP_OK; - - DIR* dir = ::opendir(dirName); - - if (dir == nullptr) { - this->m_lastError = errno; - switch (errno) { - case ENOENT: - stat = DOESNT_EXIST; - break; - case EACCES: - stat = NO_PERMISSION; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - default: - stat = OTHER_ERROR; - break; - } - return stat; - } - - this->m_dir = reinterpret_cast(dir); - return stat; - } - - Directory::Status Directory::rewind() { - - // make sure it has been opened - if (!this->isOpen()) { - return NOT_OPENED; - } - - Status stat = OP_OK; - DIR* dir = reinterpret_cast(this->m_dir); - - // no errors defined - ::rewinddir(dir); - - return stat; - } - - Directory::Status Directory::read(char * fileNameBuffer, U32 bufSize) { - - I64 dummy; - return this->read(fileNameBuffer, bufSize, dummy); - - } - - Directory::Status Directory::read(char * fileNameBuffer, U32 bufSize, I64& inode) { - - FW_ASSERT(fileNameBuffer); - - // make sure it has been opened - if (!this->isOpen()) { - return NOT_OPENED; - } - - Status stat = OP_OK; - DIR* dir = reinterpret_cast(this->m_dir); - - // Set errno to 0 so we know why we exited readdir - errno = 0; - - struct dirent *direntData = nullptr; - while ((direntData = ::readdir(dir)) != nullptr) { - // Skip hidden files - if (direntData->d_name[0] != '.') { - strncpy(fileNameBuffer, direntData->d_name, bufSize); - inode = static_cast(direntData->d_ino); - break; - } - } - - if (direntData == nullptr) { - // loop ended because readdir failed, did it error or did we run out of files? - if(errno != 0) { - // Only error from readdir is EBADF - stat = OTHER_ERROR; - this->m_lastError = errno; - } - else { - stat = NO_MORE_FILES; - } - } - - return stat; - } - - bool Directory::isOpen() { - return this->m_dir > 0; - } - - void Directory::close() { - if (this->isOpen()) { - DIR* dir = reinterpret_cast(this->m_dir); - (void)::closedir(dir); - } - this->m_dir = 0; - } - - NATIVE_INT_TYPE Directory::getLastError() { - return this->m_lastError; - } - - const char* Directory::getLastErrorString() { - return strerror(this->m_lastError); - } - -} diff --git a/Os/Linux/FileSystem.cpp b/Os/Linux/FileSystem.cpp deleted file mode 100644 index 1150f68aea..0000000000 --- a/Os/Linux/FileSystem.cpp +++ /dev/null @@ -1,586 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include // Needed for rename -#include -#include - -namespace Os { - -namespace FileSystem { - -Status createDirectory(const char* path) { - Status stat = OP_OK; - -#ifdef __VXWORKS__ - int mkStat = ::mkdir(path); -#else - int mkStat = ::mkdir(path, S_IRWXU); -#endif - - if (-1 == mkStat) { - switch (errno) { - case EACCES: - case EPERM: - case EROFS: - case EFAULT: - stat = NO_PERMISSION; - break; - case EEXIST: - stat = ALREADY_EXISTS; - break; - case ELOOP: - case ENOENT: - case ENAMETOOLONG: - stat = INVALID_PATH; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - case ENOSPC: - case EDQUOT: - stat = NO_SPACE; - break; - case EMLINK: - stat = FILE_LIMIT; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - return stat; -} // end createDirectory - -Status removeDirectory(const char* path) { - Status stat = OP_OK; - - if (::rmdir(path) == -1) { - switch (errno) { - case EACCES: - case EPERM: - case EROFS: - case EFAULT: - stat = NO_PERMISSION; - break; - case ENOTEMPTY: - case EEXIST: - stat = NOT_EMPTY; - break; - case EINVAL: - case ELOOP: - case ENOENT: - case ENAMETOOLONG: - stat = INVALID_PATH; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - case EBUSY: - stat = BUSY; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - return stat; -} // end removeDirectory - -Status readDirectory(const char* path, const U32 maxNum, Fw::String fileArray[], U32& numFiles) { - Status dirStat = OP_OK; - DIR* dirPtr = nullptr; - struct dirent* direntData = nullptr; - - FW_ASSERT(fileArray != nullptr); - FW_ASSERT(path != nullptr); - - // Open directory failed: - if ((dirPtr = ::opendir(path)) == nullptr) { - switch (errno) { - case EACCES: - dirStat = NO_PERMISSION; - break; - case ENOENT: - dirStat = INVALID_PATH; - break; - case ENOTDIR: - dirStat = NOT_DIR; - break; - default: - dirStat = OTHER_ERROR; - break; - } - return dirStat; - } - - // Set errno to 0 so we know why we exited readdir - errno = 0; - U32 arrayIdx = 0; - U32 limitCount = 0; - const U32 loopLimit = std::numeric_limits::max(); - - // Read the directory contents and store in passed in array: - while (arrayIdx < maxNum && limitCount < loopLimit) { - ++limitCount; - - if ((direntData = ::readdir(dirPtr)) != nullptr) { - // We are only care about regular files - if (direntData->d_type == DT_REG) { - FW_ASSERT(arrayIdx < maxNum, static_cast(arrayIdx), - static_cast(maxNum)); - - Fw::String str(direntData->d_name); - fileArray[arrayIdx++] = str; - } - } else { - // readdir failed, did it error or did we run out of files? - if (errno != 0) { - // Only error from readdir is EBADF - dirStat = OTHER_ERROR; - } - break; - } - } - - if (limitCount == loopLimit) { - dirStat = FILE_LIMIT; - } - - if (::closedir(dirPtr) == -1) { - // Only error from closedir is EBADF - dirStat = OTHER_ERROR; - } - - numFiles = arrayIdx; - - return dirStat; -} - -Status removeFile(const char* path) { - Status stat = OP_OK; - - if (::unlink(path) == -1) { - switch (errno) { - case EACCES: - case EPERM: - case EROFS: - stat = NO_PERMISSION; - break; - case EISDIR: - stat = IS_DIR; - break; - case ELOOP: - case ENOENT: - case ENAMETOOLONG: - stat = INVALID_PATH; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - case EBUSY: - stat = BUSY; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - return stat; -} // end removeFile - -Status moveFile(const char* originPath, const char* destPath) { - Status stat = OP_OK; - - if (::rename(originPath, destPath) == -1) { - switch (errno) { - case EACCES: - case EPERM: - case EROFS: - case EFAULT: - stat = NO_PERMISSION; - break; - case EDQUOT: - case ENOSPC: - stat = NO_SPACE; - break; - case ELOOP: - case ENAMETOOLONG: - case ENOENT: - case EINVAL: - stat = INVALID_PATH; - break; - case ENOTDIR: - case EISDIR: // Old path is not a dir - stat = NOT_DIR; - break; - case ENOTEMPTY: - case EEXIST: - stat = NOT_EMPTY; - break; - case EMLINK: - stat = FILE_LIMIT; - break; - case EBUSY: - stat = BUSY; - break; - default: - stat = OTHER_ERROR; - break; - } - } - return stat; - -} // end moveFile - -Status handleFileError(File::Status fileStatus) { - Status fileSystemStatus = OTHER_ERROR; - - switch (fileStatus) { - case File::NO_SPACE: - fileSystemStatus = NO_SPACE; - break; - case File::NO_PERMISSION: - fileSystemStatus = NO_PERMISSION; - break; - case File::DOESNT_EXIST: - fileSystemStatus = INVALID_PATH; - break; - default: - fileSystemStatus = OTHER_ERROR; - } - return fileSystemStatus; -} // end handleFileError - -/** - * A helper function that returns an "OP_OK" status if the given file - * exists and can be read from, otherwise returns an error status. - * - * If provided, will also initialize the given stat struct with file - * information. - */ -Status initAndCheckFileStats(const char* filePath, struct stat* fileInfo = nullptr) { - FileSystem::Status fs_status; - struct stat local_info; - if (!fileInfo) { - // No external struct given, so use the local one - fileInfo = &local_info; - } - - if (::stat(filePath, fileInfo) == -1) { - switch (errno) { - case EACCES: - fs_status = NO_PERMISSION; - break; - case ELOOP: - case ENOENT: - case ENAMETOOLONG: - fs_status = INVALID_PATH; - break; - case ENOTDIR: - fs_status = NOT_DIR; - break; - default: - fs_status = OTHER_ERROR; - break; - } - return fs_status; - } - - // Make sure the origin is a regular file - if (!S_ISREG(fileInfo->st_mode)) { - return INVALID_PATH; - } - - return OP_OK; -} - -/** - * A helper function that writes all the file information in the source - * file to the destination file (replaces/appends to end/etc. depending - * on destination file mode). - * - * Files must already be open and will remain open after this function - * completes. - * - * @param source File to copy data from - * @param destination File to copy data to - * @param size The number of bytes to copy - */ -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 FwSignedSizeType copyLoopLimit = (size / FILE_SYSTEM_CHUNK_SIZE) + 2; - - FwSignedSizeType loopCounter = 0; - FwSignedSizeType chunkSize; - while (loopCounter < copyLoopLimit) { - chunkSize = FILE_SYSTEM_CHUNK_SIZE; - file_status = source.read(fileBuffer, chunkSize, Os::File::WaitType::NO_WAIT); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - - if (chunkSize == 0) { - // file has been successfully copied - break; - } - - file_status = destination.write(fileBuffer, chunkSize, Os::File::WaitType::WAIT); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - loopCounter++; - } - FW_ASSERT(loopCounter < copyLoopLimit); - - return FileSystem::OP_OK; -} // end copyFileData - -Status copyFile(const char* originPath, const char* destPath) { - FileSystem::Status fs_status; - File::Status file_status; - - FwSignedSizeType fileSize = 0; - - File source; - File destination; - - fs_status = initAndCheckFileStats(originPath); - if (FileSystem::OP_OK != fs_status) { - return fs_status; - } - - // Get the file size: - fs_status = - FileSystem::getFileSize(originPath, fileSize); //!< gets the size of the file (in bytes) at location path - if (FileSystem::OP_OK != fs_status) { - return fs_status; - } - - file_status = source.open(originPath, File::OPEN_READ); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - - file_status = destination.open(destPath, File::OPEN_WRITE); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - - fs_status = copyFileData(source, destination, fileSize); - - (void)source.close(); - (void)destination.close(); - - return fs_status; -} // end copyFile - -Status appendFile(const char* originPath, const char* destPath, bool createMissingDest) { - FileSystem::Status fs_status; - File::Status file_status; - FwSignedSizeType fileSize = 0; - - File source; - File destination; - - fs_status = initAndCheckFileStats(originPath); - if (FileSystem::OP_OK != fs_status) { - return fs_status; - } - - // Get the file size: - fs_status = - FileSystem::getFileSize(originPath, fileSize); //!< gets the size of the file (in bytes) at location path - if (FileSystem::OP_OK != fs_status) { - return fs_status; - } - - file_status = source.open(originPath, File::OPEN_READ); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - - // If needed, check if destination file exists (and exit if not) - if (!createMissingDest) { - fs_status = initAndCheckFileStats(destPath); - if (FileSystem::OP_OK != fs_status) { - return fs_status; - } - } - - file_status = destination.open(destPath, File::OPEN_APPEND); - if (file_status != File::OP_OK) { - return handleFileError(file_status); - } - - fs_status = copyFileData(source, destination, fileSize); - - (void)source.close(); - (void)destination.close(); - - return fs_status; -} // end appendFile - -Status getFileSize(const char* path, FwSignedSizeType& size) { - Status fileStat = OP_OK; - struct stat fileStatStruct; - - fileStat = initAndCheckFileStats(path, &fileStatStruct); - if (FileSystem::OP_OK == fileStat) { - // Only check size if struct was initialized successfully - size = fileStatStruct.st_size; - if (static_cast(size) != fileStatStruct.st_size) { - return FileSystem::OTHER_ERROR; - } - } - - return fileStat; -} // end getFileSize - -Status changeWorkingDirectory(const char* path) { - Status stat = OP_OK; - - if (::chdir(path) == -1) { - switch (errno) { - case EACCES: - case EFAULT: - stat = NO_PERMISSION; - break; - case ENOTEMPTY: - stat = NOT_EMPTY; - break; - case ENOENT: - case ELOOP: - case ENAMETOOLONG: - stat = INVALID_PATH; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - default: - stat = OTHER_ERROR; - break; - } - } - - return stat; -} // end changeWorkingDirectory - -Status getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { - Status stat = OP_OK; - - struct statvfs fsStat; - int ret = statvfs(path, &fsStat); - if (ret) { - switch (errno) { - case EACCES: - stat = NO_PERMISSION; - break; - case ELOOP: - case ENOENT: - case ENAMETOOLONG: - stat = INVALID_PATH; - break; - case ENOTDIR: - stat = NOT_DIR; - break; - default: - stat = OTHER_ERROR; - break; - } - return stat; - } - - const FwSizeType block_size = static_cast(fsStat.f_frsize); - const FwSizeType free_blocks = static_cast(fsStat.f_bfree); - const FwSizeType total_blocks = static_cast(fsStat.f_blocks); - - // Check for casting and type error - if (((block_size <= 0) || (static_cast(block_size) != fsStat.f_frsize)) || - ((free_blocks <= 0) || (static_cast(free_blocks) != fsStat.f_bfree)) || - ((total_blocks <= 0) || (static_cast(block_size) != fsStat.f_blocks))) { - return OTHER_ERROR; - } - // Check for overflow in multiplication - if (free_blocks > (std::numeric_limits::max() / block_size) || - total_blocks > (std::numeric_limits::max() / block_size)) { - return OTHER_ERROR; - } - freeBytes = free_blocks * block_size; - totalBytes = total_blocks * block_size; - return stat; -} - -// Public function to get the file count for a given directory. -Status getFileCount(const char* directory, U32& fileCount) { - Status dirStat = OP_OK; - DIR* dirPtr = nullptr; - struct dirent* direntData = nullptr; - U32 limitCount; - const U32 loopLimit = std::numeric_limits::max(); - - fileCount = 0; - if ((dirPtr = ::opendir(directory)) == nullptr) { - switch (errno) { - case EACCES: - dirStat = NO_PERMISSION; - break; - case ENOENT: - dirStat = INVALID_PATH; - break; - case ENOTDIR: - dirStat = NOT_DIR; - break; - default: - dirStat = OTHER_ERROR; - break; - } - return dirStat; - } - - // Set errno to 0 so we know why we exited readdir - errno = 0; - for (limitCount = 0; limitCount < loopLimit; limitCount++) { - if ((direntData = ::readdir(dirPtr)) != nullptr) { - // We are only counting regular files - if (direntData->d_type == DT_REG) { - fileCount++; - } - } else { - // readdir failed, did it error or did we run out of files? - if (errno != 0) { - // Only error from readdir is EBADF - dirStat = OTHER_ERROR; - } - break; - } - } - if (limitCount == loopLimit) { - dirStat = FILE_LIMIT; - } - - if (::closedir(dirPtr) == -1) { - // Only error from closedir is EBADF - dirStat = OTHER_ERROR; - } - - return dirStat; -} // end getFileCount - -} // namespace FileSystem - -} // namespace Os diff --git a/Os/Models/CMakeLists.txt b/Os/Models/CMakeLists.txt index a3b46cfaad..1d3d0a408e 100644 --- a/Os/Models/CMakeLists.txt +++ b/Os/Models/CMakeLists.txt @@ -10,5 +10,7 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/File.fpp" "${CMAKE_CURRENT_LIST_DIR}/Task.fpp" "${CMAKE_CURRENT_LIST_DIR}/Mutex.fpp" + "${CMAKE_CURRENT_LIST_DIR}/Directory.fpp" + "${CMAKE_CURRENT_LIST_DIR}/FileSystem.fpp" ) register_fprime_module() diff --git a/Os/Models/Directory.fpp b/Os/Models/Directory.fpp new file mode 100644 index 0000000000..a2b2768fef --- /dev/null +++ b/Os/Models/Directory.fpp @@ -0,0 +1,27 @@ +# ====================================================================== +# \title Os/Models/Directory.fpp +# \brief FPP type definitions for Os/Directory.hpp concepts +# ====================================================================== + +module Os { +@ FPP shadow-enum representing Os::Directory::Status +enum DirectoryStatus { + OP_OK, @< Operation was successful + DOESNT_EXIST, @< Directory doesn't exist + NO_PERMISSION, @< No permission to read directory + NOT_OPENED, @< Directory hasn't been opened yet + NOT_DIR, @< Path is not a directory + NO_MORE_FILES, @< Directory stream has no more files + FILE_LIMIT, @< Directory has more files than can be read + BAD_DESCRIPTOR, @< Directory stream descriptor is invalid + ALREADY_EXISTS, @< Directory already exists + NOT_SUPPORTED, @< Operation is not supported by the current implementation + OTHER_ERROR, @< A catch-all for other errors. Have to look in implementation-specific code +} + +enum DirectoryOpenMode { + READ, @< Error if directory doesn't exist + CREATE_IF_MISSING, @< Create directory if it doesn't exist + CREATE_EXCLUSIVE, @< Create directory and error if it already exists +} +} diff --git a/Os/Models/FileSystem.fpp b/Os/Models/FileSystem.fpp new file mode 100644 index 0000000000..39720f26ff --- /dev/null +++ b/Os/Models/FileSystem.fpp @@ -0,0 +1,28 @@ +# ====================================================================== +# \title Os/Models/FileSystem.fpp +# \brief FPP type definitions for Os/FileSystem.hpp concepts +# ====================================================================== + +module Os { +@ FPP shadow-enum representing Os::FileSystem::Status +enum FileSystemStatus { + OP_OK, @< Operation was successful + ALREADY_EXISTS, @< File already exists + NO_SPACE, @< No space left + NO_PERMISSION, @< No permission to write + NOT_DIR, @< Path is not a directory + IS_DIR, @< Path is a directory + NOT_EMPTY, @< directory is not empty + INVALID_PATH, @< Path is too long, too many sym links, etc. + DOESNT_EXIST, @< Path doesn't exist + FILE_LIMIT, @< Too many files or links + BUSY, @< Operand is in use by the system or by a process + NO_MORE_FILES, @< Directory stream has no more files + BUFFER_TOO_SMALL, @< Buffer size is too small to hold full path (for getWorkingDirectory) + EXDEV_ERROR, @< Operation not supported across devices (e.g. rename) + OVERFLOW_ERROR, @< Operation failed due to overflow in calculation of the result + NOT_SUPPORTED, @< Operation is not supported by the current implementation + OTHER_ERROR, @< other OS-specific error +} + +} diff --git a/Os/Models/Models.hpp b/Os/Models/Models.hpp index 680336c7f6..c3741da482 100644 --- a/Os/Models/Models.hpp +++ b/Os/Models/Models.hpp @@ -3,16 +3,21 @@ // \brief header used to validate Os/Models before use // ====================================================================== +#ifndef OS_MODELS_MODELS_HPP +#define OS_MODELS_MODELS_HPP + #include "Os/Models/FileStatusEnumAc.hpp" #include "Os/Models/FileModeEnumAc.hpp" #include "Os/Models/TaskStatusEnumAc.hpp" #include "Os/Models/MutexStatusEnumAc.hpp" +#include "Os/Models/DirectoryStatusEnumAc.hpp" +#include "Os/Models/DirectoryOpenModeEnumAc.hpp" +#include "Os/Models/FileSystemStatusEnumAc.hpp" #include "Os/File.hpp" #include "Os/Task.hpp" #include "Os/Mutex.hpp" - -#ifndef OS_MODELS_MODELS_HPP -#define OS_MODELS_MODELS_HPP +#include "Os/Directory.hpp" +#include "Os/FileSystem.hpp" // Check consistency of every constant in the Os::File::Status enum static_assert(static_cast(Os::File::Status::MAX_STATUS) == @@ -90,4 +95,79 @@ static_assert(static_cast(Os::Mutex::Status::ERROR_DEADLOCK) static_assert(static_cast(Os::Mutex::Status::ERROR_OTHER) == Os::MutexStatus::T::ERROR_OTHER, "Mutex status and FPP shadow enum do not match"); + +// Check consistency of every constant in the Os::Directory::Status enum +static_assert(static_cast(Os::Directory::Status::OP_OK) == Os::DirectoryStatus::T::OP_OK, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::DOESNT_EXIST) == Os::DirectoryStatus::T::DOESNT_EXIST, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::NO_PERMISSION) == Os::DirectoryStatus::T::NO_PERMISSION, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::NOT_OPENED) == Os::DirectoryStatus::T::NOT_OPENED, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::NOT_DIR) == Os::DirectoryStatus::T::NOT_DIR, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::NO_MORE_FILES) == Os::DirectoryStatus::T::NO_MORE_FILES, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::FILE_LIMIT) == Os::DirectoryStatus::T::FILE_LIMIT, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::BAD_DESCRIPTOR) == Os::DirectoryStatus::T::BAD_DESCRIPTOR, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::ALREADY_EXISTS) == Os::DirectoryStatus::T::ALREADY_EXISTS, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::NOT_SUPPORTED) == Os::DirectoryStatus::T::NOT_SUPPORTED, + "Directory status and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::Status::OTHER_ERROR) == Os::DirectoryStatus::T::OTHER_ERROR, + "Directory status and FPP shadow enum do not match"); + + +// Check consistency of every constant in the Os::Directory::Mode enum +static_assert(static_cast(Os::Directory::OpenMode::MAX_OPEN_MODE) == + static_cast(Os::DirectoryOpenMode::NUM_CONSTANTS), + "File mode and FPP shadow enum have inconsistent number of values"); +static_assert(static_cast(Os::Directory::OpenMode::READ) == Os::DirectoryOpenMode::T::READ, + "Directory mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::OpenMode::CREATE_IF_MISSING) == Os::DirectoryOpenMode::T::CREATE_IF_MISSING, + "Directory mode and FPP shadow enum do not match"); +static_assert(static_cast(Os::Directory::OpenMode::CREATE_EXCLUSIVE) == Os::DirectoryOpenMode::T::CREATE_EXCLUSIVE, + "Directory mode and FPP shadow enum do not match"); + + +// Check consistency of every constant in the Os::FileSystem::Status enum +static_assert(static_cast(Os::FileSystem::Status::OP_OK) == Os::FileSystemStatus::T::OP_OK, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::ALREADY_EXISTS) == Os::FileSystemStatus::T::ALREADY_EXISTS, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NO_SPACE) == Os::FileSystemStatus::T::NO_SPACE, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NO_PERMISSION) == Os::FileSystemStatus::T::NO_PERMISSION, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NOT_DIR) == Os::FileSystemStatus::T::NOT_DIR, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::IS_DIR) == Os::FileSystemStatus::T::IS_DIR, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NOT_EMPTY) == Os::FileSystemStatus::T::NOT_EMPTY, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::INVALID_PATH) == Os::FileSystemStatus::T::INVALID_PATH, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::DOESNT_EXIST) == Os::FileSystemStatus::T::DOESNT_EXIST, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::FILE_LIMIT) == Os::FileSystemStatus::T::FILE_LIMIT, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::BUSY) == Os::FileSystemStatus::T::BUSY, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NO_MORE_FILES) == Os::FileSystemStatus::T::NO_MORE_FILES, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::BUFFER_TOO_SMALL) == Os::FileSystemStatus::T::BUFFER_TOO_SMALL, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::EXDEV_ERROR) == Os::FileSystemStatus::T::EXDEV_ERROR, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::OVERFLOW_ERROR) == Os::FileSystemStatus::T::OVERFLOW_ERROR, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::NOT_SUPPORTED) == Os::FileSystemStatus::T::NOT_SUPPORTED, + "FileSystem status and FPP shadow enum do not match"); +static_assert(static_cast(Os::FileSystem::Status::OTHER_ERROR) == Os::FileSystemStatus::T::OTHER_ERROR, + "FileSystem status and FPP shadow enum do not match"); + + #endif // OS_MODELS_MODELS_HPP diff --git a/Os/Mutex.hpp b/Os/Mutex.hpp index e46fae04f8..1a12c58906 100644 --- a/Os/Mutex.hpp +++ b/Os/Mutex.hpp @@ -13,7 +13,6 @@ namespace Os { struct MutexHandle {}; class MutexInterface { - // add enum with public: enum Status { OP_OK, //!< Operation was successful @@ -39,7 +38,7 @@ class MutexInterface { virtual MutexHandle* getHandle() = 0; //! \brief provide a pointer to a Mutex delegate object - static MutexInterface* getDelegate(HandleStorage& aligned_new_memory); // TODO + static MutexInterface* getDelegate(HandleStorage& aligned_new_memory); virtual Status take() = 0; //!< lock the mutex return status virtual Status release() = 0; //!< unlock the mutex return status diff --git a/Os/Os.cpp b/Os/Os.cpp new file mode 100644 index 0000000000..7fb93b2f46 --- /dev/null +++ b/Os/Os.cpp @@ -0,0 +1,18 @@ +// ====================================================================== +// \title Os/Os.cpp +// \brief common definitions for the OSAL layer +// ====================================================================== +#include "Os/Os.hpp" +#include "FpConfig.h" +#include "Os/Console.hpp" +#include "Os/FileSystem.hpp" + +namespace Os { + +void init() { + // Console and FileSystem are singletons and must be initialized + (void)Os::Console::init(); + (void)Os::FileSystem::init(); +} + +} // namespace Os diff --git a/Os/Os.hpp b/Os/Os.hpp index af8adce9fb..1a68c13165 100644 --- a/Os/Os.hpp +++ b/Os/Os.hpp @@ -8,4 +8,15 @@ //! Storage type for OSAL handles typedef U8 HandleStorage[FW_HANDLE_MAX_SIZE]; + +typedef U8 DirectoryHandleStorage[FW_DIRECTORY_HANDLE_MAX_SIZE]; +typedef U8 FileSystemHandleStorage[FW_FILESYSTEM_HANDLE_MAX_SIZE]; + +namespace Os { + +//! \brief Initialization function for the OSAL layer +void init(); + +} // namespace Os + #endif diff --git a/Os/Posix/CMakeLists.txt b/Os/Posix/CMakeLists.txt index 9502490067..7070077c91 100644 --- a/Os/Posix/CMakeLists.txt +++ b/Os/Posix/CMakeLists.txt @@ -8,7 +8,10 @@ restrict_platforms(Posix) add_custom_target("${FPRIME_CURRENT_MODULE}") -#### Shared Section #### +# ----------------------------------------- +## Shared Section +# ----------------------------------------- + set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/error.cpp" ) @@ -17,7 +20,9 @@ set(HEADER_FILES ) register_fprime_module(Os_Posix_Shared) -#### Os/File/Posix Section #### +# ----------------------------------------- +### Os/File/Posix Section +# ----------------------------------------- set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/File.cpp" ) @@ -28,7 +33,23 @@ set(MOD_DEPS Os_Posix_Shared) register_fprime_module(Os_File_Posix) register_fprime_implementation(Os/File Os_File_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultFile.cpp") -#### Os/Console/Posix Section #### +#### Os/File/Posix Unit Tests #### +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) + +# ----------------------------------------- +### Os/Console/Posix Section +# ----------------------------------------- set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Console.cpp" ) @@ -39,7 +60,7 @@ set(MOD_DEPS) register_fprime_module(Os_Console_Posix) register_fprime_implementation(Os/Console Os_Console_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultConsole.cpp") -#### Os/Task/Posix Unit Tests #### +#### Os/Console/Posix Unit Tests #### set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixConsoleTests.cpp" ) @@ -49,21 +70,11 @@ set(UT_MOD_DEPS choose_fprime_implementation(Os/Console Os_Console_Posix) register_fprime_ut(PosixConsoleTest) -#### Os/File/Posix Unit Tests #### -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) -#### Os/Task/Posix Section #### +# ----------------------------------------- +### Os/Task/Posix Section +# ----------------------------------------- + set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Task.cpp" ) @@ -88,7 +99,9 @@ set(UT_MOD_DEPS choose_fprime_implementation(Os/Task Os_Task_Posix) register_fprime_ut(PosixTaskTest) -#### Os/Mutex/Posix Section #### +# ----------------------------------------- +### Os/Mutex/Posix Section +# ----------------------------------------- set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Mutex.cpp" ) @@ -111,3 +124,57 @@ set(UT_MOD_DEPS ) choose_fprime_implementation(Os/Mutex Os_Mutex_Posix) register_fprime_ut(PosixMutexTest) + + +# ----------------------------------------- +### Os/FileSystem/Posix Section +# ----------------------------------------- +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/FileSystem.cpp" +) +set(HEADER_FILES + "${CMAKE_CURRENT_LIST_DIR}/FileSystem.hpp" +) +set(MOD_DEPS Os_Posix_Shared) +register_fprime_module(Os_FileSystem_Posix) +register_fprime_implementation(Os/FileSystem Os_FileSystem_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultFileSystem.cpp") + +#### Os/FileSystem/Posix Unit Tests #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/filesystem/CommonTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/filesystem/FileSystemRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixFileSystemTests.cpp" +) +set(UT_MOD_DEPS + Os + STest +) +choose_fprime_implementation(Os/FileSystem Os_FileSystem_Posix) +register_fprime_ut(PosixFileSystemTest) + +# ----------------------------------------- +### Os/Directory/Posix Section +# ----------------------------------------- +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/Directory.cpp" +) +set(HEADER_FILES + "${CMAKE_CURRENT_LIST_DIR}/Directory.hpp" +) +set(MOD_DEPS Os_Posix_Shared) +register_fprime_module(Os_Directory_Posix) +register_fprime_implementation(Os/Directory Os_Directory_Posix "${CMAKE_CURRENT_LIST_DIR}/DefaultDirectory.cpp") + +#### Os/Directory/Posix Unit Tests #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/directory/CommonTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/../test/ut/directory/DirectoryRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixDirectoryTests.cpp" +) +set(UT_MOD_DEPS + Os + STest +) +choose_fprime_implementation(Os/Directory Os_Directory_Posix) +register_fprime_ut(PosixDirectoryTest) + diff --git a/Os/Posix/Console.cpp b/Os/Posix/Console.cpp index 477495ab45..46e53d85fe 100644 --- a/Os/Posix/Console.cpp +++ b/Os/Posix/Console.cpp @@ -19,6 +19,7 @@ void PosixConsole::writeMessage(const CHAR *message, const FwSizeType size) { FwSizeType capped_size = (size <= std::numeric_limits::max()) ? size : std::numeric_limits::max(); if (message != nullptr) { (void)::fwrite(message, sizeof(CHAR), static_cast(capped_size), this->m_handle.m_file_descriptor); + (void)::fflush(this->m_handle.m_file_descriptor); } } diff --git a/Os/Posix/DefaultDirectory.cpp b/Os/Posix/DefaultDirectory.cpp new file mode 100644 index 0000000000..abde91735e --- /dev/null +++ b/Os/Posix/DefaultDirectory.cpp @@ -0,0 +1,13 @@ +// ====================================================================== +// \title Os/Posix/DefaultDirectory.cpp +// \brief sets default Os::Directory to posix implementation via linker +// ====================================================================== +#include "Os/Directory.hpp" +#include "Os/Posix/Directory.hpp" +#include "Os/Delegate.hpp" + +namespace Os { +DirectoryInterface* DirectoryInterface::getDelegate(HandleStorage& aligned_new_memory) { + return Os::Delegate::makeDelegate(aligned_new_memory); +} +} diff --git a/Os/Posix/DefaultFileSystem.cpp b/Os/Posix/DefaultFileSystem.cpp new file mode 100644 index 0000000000..6f88c21595 --- /dev/null +++ b/Os/Posix/DefaultFileSystem.cpp @@ -0,0 +1,13 @@ +// ====================================================================== +// \title Os/Posix/DefaultFileSystem.cpp +// \brief sets default Os::FileSystem to posix implementation via linker +// ====================================================================== +#include "Os/FileSystem.hpp" +#include "Os/Posix/FileSystem.hpp" +#include "Os/Delegate.hpp" + +namespace Os { +FileSystemInterface* FileSystemInterface::getDelegate(HandleStorage& aligned_new_memory) { + return Os::Delegate::makeDelegate(aligned_new_memory); +} +} diff --git a/Os/Posix/Directory.cpp b/Os/Posix/Directory.cpp new file mode 100644 index 0000000000..5a32b9bc45 --- /dev/null +++ b/Os/Posix/Directory.cpp @@ -0,0 +1,101 @@ +// ====================================================================== +// \title Os/Posix/Directory.cpp +// \brief Posix implementation for Os::Directory +// ====================================================================== +#include +#include +#include + +#include +#include +#include +#include + +namespace Os { +namespace Posix { +namespace Directory { + +PosixDirectory::PosixDirectory() : Os::DirectoryInterface(), m_handle() {} + +DirectoryHandle* PosixDirectory::getHandle() { + return &this->m_handle; +} + +PosixDirectory::Status PosixDirectory::open(const char* path, OpenMode mode) { + Status status = Status::OP_OK; + + // If one of the CREATE mode, attempt to create the directory + if (mode == OpenMode::CREATE_EXCLUSIVE || mode == OpenMode::CREATE_IF_MISSING) { + if (::mkdir(path, S_IRWXU) == -1) { + status = errno_to_directory_status(errno); + // If error is not ALREADY_EXISTS, return the error + // If any error and mode CREATE_EXCLUSIVE, return the error + // Else, we keep going with OP_OK + if (status != Status::ALREADY_EXISTS || mode == OpenMode::CREATE_EXCLUSIVE) { + return status; + } else { + status = Status::OP_OK; + } + } + } + + DIR* dir = ::opendir(path); + + if (dir == nullptr) { + status = errno_to_directory_status(errno); + } + + this->m_handle.m_dir_descriptor = dir; + return status; +} + +PosixDirectory::Status PosixDirectory::rewind() { + Status status = Status::OP_OK; + // no errors defined in man page for rewinddir + ::rewinddir(this->m_handle.m_dir_descriptor); + return status; +} + +PosixDirectory::Status PosixDirectory::read(char* fileNameBuffer, FwSizeType bufSize) { + FW_ASSERT(fileNameBuffer); + + Status status = Status::OP_OK; + + // Set errno to 0 so we know why we exited readdir + // This is recommended by the manual pages (man 3 readdir) + errno = 0; + + struct dirent* direntData = nullptr; + while ((direntData = ::readdir(this->m_handle.m_dir_descriptor)) != nullptr) { + // Skip . and .. directory entries + if ((direntData->d_name[0] == '.' and direntData->d_name[1] == '\0') or + (direntData->d_name[0] == '.' and direntData->d_name[1] == '.' and direntData->d_name[2] == '\0')) { + continue; + } else { + (void)Fw::StringUtils::string_copy(fileNameBuffer, direntData->d_name, bufSize); + break; + } + } + if (direntData == nullptr) { + // loop ended because readdir failed, did it error or did we run out of files? + if (errno != 0) { + // Only error from readdir is EBADF + status = Status::BAD_DESCRIPTOR; + } else { + status = Status::NO_MORE_FILES; + } + } + return status; +} + +void PosixDirectory::close() { + // ::closedir errors if dir descriptor is nullptr + if (this->m_handle.m_dir_descriptor != nullptr) { + (void)::closedir(this->m_handle.m_dir_descriptor); + } + this->m_handle.m_dir_descriptor = nullptr; +} + +} // namespace Directory +} // namespace Posix +} // namespace Os diff --git a/Os/Posix/Directory.hpp b/Os/Posix/Directory.hpp new file mode 100644 index 0000000000..50a53e2f30 --- /dev/null +++ b/Os/Posix/Directory.hpp @@ -0,0 +1,87 @@ +// ====================================================================== +// \title Os/Posix/Directory.hpp +// \brief Posix definitions for Os::Directory +// ====================================================================== +#ifndef OS_POSIX_DIRECTORY_HPP +#define OS_POSIX_DIRECTORY_HPP +#include +#include + +namespace Os { +namespace Posix { +namespace Directory { + +struct PosixDirectoryHandle : public DirectoryHandle { + DIR* m_dir_descriptor = nullptr; +}; + +//! \brief Posix implementation of Os::Directory +//! +//! Posix implementation of `DirectoryInterface` for use as a delegate class handling error-only file operations. +class PosixDirectory : public DirectoryInterface { + public: + //! \brief constructor + PosixDirectory(); + + //! \brief destructor + ~PosixDirectory() = default; + + //! \brief return the underlying mutex handle (implementation specific) + //! \return internal mutex handle representation + DirectoryHandle* getHandle() override; + + // ------------------------------------------------------------ + // Implementation-specific Directory member functions + // ------------------------------------------------------------ + + //! \brief Open or create a directory + //! + //! Using the path provided, this function will open or create a directory. + //! Use OpenMode::READ to open an existing directory and error if the directory is not found + //! Use OpenMode::CREATE_IF_MISSING to open a directory, creating the directory if it doesn't exist + //! Use OpenMode::CREATE_EXCLUSIVE to open a directory, creating the directory and erroring if it already exists + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! + //! \param path: path of directory to open + //! \param mode: enum (READ, CREATE_IF_MISSING, CREATE_EXCLUSIVE). See notes above for more information + //! \return status of the operation + Status open(const char* path, OpenMode mode) override; + + //! \brief Check if Directory is open or not + //! \return true if Directory is open, false otherwise + bool isOpen(); + + //! \brief Rewind directory stream + //! + //! Each read operation moves the seek position forward. This function resets the seek position to the beginning. + //! + //! \return status of the operation + Status rewind() override; + + //! \brief Get next filename from directory stream + //! + //! Writes at most buffSize characters of the file name to fileNameBuffer. + //! This function skips the current directory (.) and parent directory (..) entries. + //! Returns NO_MORE_FILES if there are no more files to read from the buffer. + //! + //! It is invalid to pass `nullptr` as fileNameBuffer. + //! + //! \param fileNameBuffer: buffer to store filename + //! \param buffSize: size of fileNameBuffer + //! \return status of the operation + Status read(char* fileNameBuffer, FwSizeType buffSize) override; + + //! \brief Close directory + void close() override; + + private: + //! Handle for PosixDirectory + PosixDirectoryHandle m_handle; +}; + +} // namespace Directory +} // namespace Posix +} // namespace Os +#endif // OS_POSIX_DIRECTORY_HPP diff --git a/Os/Posix/FileSystem.cpp b/Os/Posix/FileSystem.cpp new file mode 100644 index 0000000000..c841470f51 --- /dev/null +++ b/Os/Posix/FileSystem.cpp @@ -0,0 +1,92 @@ +// ====================================================================== +// \title Os/Posix/FileSystem.cpp +// \brief Posix implementation for Os::FileSystem +// ====================================================================== +#include "Os/Posix/FileSystem.hpp" +#include "Os/Posix/error.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace Os { +namespace Posix { +namespace FileSystem { + + +PosixFileSystem::Status PosixFileSystem::_removeDirectory(const char* path) { + Status status = OP_OK; + if (::rmdir(path) == -1) { + status = errno_to_filesystem_status(errno); + } + return status; +} + +PosixFileSystem::Status PosixFileSystem::_removeFile(const char* path) { + Status status = OP_OK; + if (::unlink(path) == -1) { + status = errno_to_filesystem_status(errno); + } + return status; +} + +PosixFileSystem::Status PosixFileSystem::_rename(const char* originPath, const char* destPath) { + Status status = OP_OK; + if (::rename(originPath, destPath) == -1) { + status = errno_to_filesystem_status(errno); + } + return status; +} + +PosixFileSystem::Status PosixFileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) { + Status status = OP_OK; + if (::getcwd(path, bufferSize) == nullptr) { + status = errno_to_filesystem_status(errno); + } + return status; +} + +PosixFileSystem::Status PosixFileSystem::_changeWorkingDirectory(const char* path) { + Status status = OP_OK; + if (::chdir(path) == -1) { + status = errno_to_filesystem_status(errno); + } + return status; +} + +PosixFileSystem::Status PosixFileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { + Status stat = OP_OK; + static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "FwSizeType must be able to hold fsblkcnt_t"); + static_assert(std::numeric_limits::min() <= std::numeric_limits::min(), "FwSizeType must be able to hold fsblkcnt_t"); + static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "FwSizeType must be able to hold unsigned long"); + struct statvfs fsStat; + int ret = statvfs(path, &fsStat); + if (ret) { + return errno_to_filesystem_status(errno); + } + + const FwSizeType block_size = static_cast(fsStat.f_frsize); + const FwSizeType free_blocks = static_cast(fsStat.f_bfree); + const FwSizeType total_blocks = static_cast(fsStat.f_blocks); + + // Check for overflow in multiplication + if (free_blocks > (std::numeric_limits::max() / block_size) || + total_blocks > (std::numeric_limits::max() / block_size)) { + return OVERFLOW_ERROR; + } + freeBytes = free_blocks * block_size; + totalBytes = total_blocks * block_size; + return stat; +} + +FileSystemHandle* PosixFileSystem::getHandle() { + return &this->m_handle; +} + +} // namespace File +} // namespace Posix +} // namespace Os diff --git a/Os/Posix/FileSystem.hpp b/Os/Posix/FileSystem.hpp new file mode 100644 index 0000000000..ba5b4d3f46 --- /dev/null +++ b/Os/Posix/FileSystem.hpp @@ -0,0 +1,107 @@ +// ====================================================================== +// \title Os/Posix/FileSystem.hpp +// \brief Posix FileSystem definitions for Os::FileSystem +// ====================================================================== +#ifndef OS_POSIX_FILESYSTEM_HPP +#define OS_POSIX_FILESYSTEM_HPP + +#include "Os/FileSystem.hpp" + +namespace Os { +namespace Posix { +namespace FileSystem { + +struct PosixFileSystemHandle : public FileSystemHandle {}; + +//! \brief Posix implementation of Os::FileSystem +//! +//! Posix implementation of `FileSystemInterface` for use as a delegate class handling error-only fileSystem operations. +//! +class PosixFileSystem : public FileSystemInterface { + public: + //! \brief constructor + PosixFileSystem() = default; + + //! \brief destructor + ~PosixFileSystem() override = default; + + // ------------------------------------------------------------ + // Implementation-specific member functions - overrides + // ------------------------------------------------------------ + + //! \brief Remove a directory at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the directory to remove + //! \return Status of the operation + Status _removeDirectory(const char* path) override; + + //! \brief Remove a file at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file to remove + //! \return Status of the operation + Status _removeFile(const char* path) override; + + //! \brief Rename a file from source to destination + //! + //! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR + //! and moveFile can be used instead to force a copy-and-remove. + //! + //! It is invalid to pass `nullptr` as sourcePath or destPath. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + Status _rename(const char* sourcePath, const char* destPath) override; + + //! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path on the filesystem to query + //! \param totalBytes Reference to store the total bytes on the filesystem + //! \param freeBytes Reference to store the free bytes on the filesystem + //! \return Status of the operation + Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) override; + + //! \brief Get the current working directory + //! + //! Writes the current working directory path to the provided buffer of size bufferSize. + //! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL. + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to pass a bufferSize of 0. + //! + //! \param path Buffer to store the current working directory path + //! \param bufferSize Size of the buffer + //! \return Status of the operation + Status _getWorkingDirectory(char* path, FwSizeType bufferSize) override; + + //! \brief Change the current working directory to the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the new working directory + //! \return Status of the operation + Status _changeWorkingDirectory(const char* path) override; + + //! \brief Get the raw FileSystem handle + //! + //! Gets the raw FileSystem 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 fileSystem handle + FileSystemHandle* getHandle() override; + + private: + //! FileSystem handle for PosixFileSystem + PosixFileSystemHandle m_handle; +}; // class PosixFileSystem + +} // namespace FileSystem +} // namespace Posix +} // namespace Os +#endif // OS_POSIX_FILESYSTEM_HPP diff --git a/Os/Posix/error.cpp b/Os/Posix/error.cpp index 0672cc55ed..9932b6be22 100644 --- a/Os/Posix/error.cpp +++ b/Os/Posix/error.cpp @@ -48,6 +48,86 @@ File::Status errno_to_file_status(PlatformIntType errno_input) { return status; } +FileSystem::Status errno_to_filesystem_status(PlatformIntType errno_input) { + FileSystem::Status status = FileSystem::Status::OP_OK; + switch (errno_input) { + case 0: + status = FileSystem::Status::OP_OK; + break; + // All fall through are intended to fallback on OTHER_ERROR + case EACCES: + status = FileSystem::Status::NO_PERMISSION; + break; + case EPERM: + case EROFS: + case EFAULT: + status = FileSystem::Status::NO_PERMISSION; + break; + case EEXIST: + status = FileSystem::Status::ALREADY_EXISTS; + break; + case ELOOP: + case ENOENT: + status = FileSystem::Status::DOESNT_EXIST; + break; + case ENAMETOOLONG: + status = FileSystem::Status::INVALID_PATH; + break; + case ENOTDIR: + status = FileSystem::Status::NOT_DIR; + break; + case EDQUOT: + status = FileSystem::Status::NO_SPACE; + break; + case EMLINK: + status = FileSystem::Status::FILE_LIMIT; + break; + case ENOSPC: + case EFBIG: + status = FileSystem::Status::NO_SPACE; + break; + case ENOSYS: + case EOPNOTSUPP: + status = FileSystem::Status::NOT_SUPPORTED; + break; + case ERANGE: + status = FileSystem::Status::BUFFER_TOO_SMALL; + break; + case EXDEV: + status = FileSystem::Status::EXDEV_ERROR; + break; + default: + status = FileSystem::Status::OTHER_ERROR; + break; + } + return status; +} + +Directory::Status errno_to_directory_status(PlatformIntType errno_input) { + Directory::Status status = Directory::Status::OP_OK; + switch (errno_input) { + case 0: + status = Directory::Status::OP_OK; + break; + case ENOENT: + status = Directory::Status::DOESNT_EXIST; + break; + case EACCES: + status = Directory::Status::NO_PERMISSION; + break; + case ENOTDIR: + status = Directory::Status::NOT_DIR; + break; + case EEXIST: + status = Directory::Status::ALREADY_EXISTS; + break; + default: + status = Directory::Status::OTHER_ERROR; + break; + } + return status; +} + Task::Status posix_status_to_task_status(PlatformIntType posix_status) { Task::Status status = Task::Status::OP_OK; switch (posix_status) { diff --git a/Os/Posix/error.hpp b/Os/Posix/error.hpp index 55e5c44895..afacabaa8f 100644 --- a/Os/Posix/error.hpp +++ b/Os/Posix/error.hpp @@ -2,10 +2,12 @@ // \title Os/Posix/error.hpp // \brief header for posix errno conversion // ====================================================================== -#include "Os/File.hpp" -#include "Os/Task.hpp" #ifndef OS_POSIX_ERRNO_HPP #define OS_POSIX_ERRNO_HPP +#include "Os/File.hpp" +#include "Os/Task.hpp" +#include "Os/FileSystem.hpp" +#include "Os/Directory.hpp" namespace Os { namespace Posix { @@ -16,6 +18,18 @@ namespace Posix { //! Os::File::Status errno_to_file_status(PlatformIntType errno_input); +//! Convert an errno representation of an error to the Os::FileSystem::Status representation. +//! \param errno_input: errno representation of the error +//! \return: Os::File::Status representation of the error +//! +Os::FileSystem::Status errno_to_filesystem_status(PlatformIntType errno_input); + +//! Convert an errno representation of an error to the Os::FileSystem::Status representation. +//! \param errno_input: errno representation of the error +//! \return: Os::File::Status representation of the error +//! +Os::Directory::Status errno_to_directory_status(PlatformIntType errno_input); + //! Convert an posix task representation of an error to the Os::Task::Status representation. //! \param posix_status: errno representation of the error //! \return: Os::Task::Status representation of the error diff --git a/Os/Posix/test/ut/PosixDirectoryTests.cpp b/Os/Posix/test/ut/PosixDirectoryTests.cpp new file mode 100644 index 0000000000..c42eda3391 --- /dev/null +++ b/Os/Posix/test/ut/PosixDirectoryTests.cpp @@ -0,0 +1,67 @@ +// ====================================================================== +// \title Os/Posix/test/ut/PosixDirectoryTests.cpp +// \brief tests for posix implementation for Os::Directory +// ====================================================================== +#include "Os/test/ut/directory/RulesHeaders.hpp" +#include "Os/test/ut/directory/CommonTests.hpp" +#include "Os/Posix/Task.hpp" +#include +#include "STest/Scenario/Scenario.hpp" +#include "STest/Pick/Pick.hpp" +#include "Fw/Types/String.hpp" + +#include // for ::open() + +namespace Os { +namespace Test { +namespace Directory { + + +//! Maximum number of files per test directory +//! Intentionally low to have a decent probability of having an empty directory +static const FwIndexType MAX_FILES_PER_DIRECTORY = 4; +static const std::string FILENAME_PREFIX = "test_file_"; +static const std::string TEST_DIRECTORY_PATH = "./test_directory"; + +//! Set up function as defined by the unit test implementor +void setUp(Os::Test::Directory::Tester* tester) { + tester->m_path = TEST_DIRECTORY_PATH; + ::mkdir(tester->m_path.c_str(), 0777); + // Files are named test_file_0, test_file_1, ... + std::vector filenames; + // fileCount can be 0 (empty directory) + FwSizeType fileCount = STest::Pick::lowerUpper(0, MAX_FILES_PER_DIRECTORY); + for (FwSizeType i = 0; i < fileCount; i++) { + tester->m_filenames.push_back(FILENAME_PREFIX + std::to_string(i)); + } + for (auto filename : tester->m_filenames) { + ::open((tester->m_path + "/" + filename).c_str(), O_CREAT); + } +} + +//! Tear down function as defined by the unit test implementor +void tearDown(Os::Test::Directory::Tester* tester) { + for (auto filename : tester->m_filenames) { + ::unlink((tester->m_path + "/" + filename).c_str()); + } + ::rmdir(tester->m_path.c_str()); +} + +} // namespace Test +} // namespace FileSystem +} // namespace Os + + + + +// ---------------------------------------------------------------------- +// Posix Test Cases: +// +// No posix-specific tests - tests are pulled in from CommonTests.cpp +// ---------------------------------------------------------------------- + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Os/Posix/test/ut/PosixFileSystemTests.cpp b/Os/Posix/test/ut/PosixFileSystemTests.cpp new file mode 100644 index 0000000000..d504aca364 --- /dev/null +++ b/Os/Posix/test/ut/PosixFileSystemTests.cpp @@ -0,0 +1,23 @@ +// ====================================================================== +// \title Os/Posix/test/ut/PosixFileSystemTests.cpp +// \brief tests for posix implementation for Os::FileSystem +// ====================================================================== +#include "Os/test/ut/filesystem/RulesHeaders.hpp" +#include "Os/test/ut/filesystem/CommonTests.hpp" +#include "Os/Posix/Task.hpp" +#include +#include "STest/Scenario/Scenario.hpp" +#include "STest/Pick/Pick.hpp" +#include "Fw/Types/String.hpp" + +// ---------------------------------------------------------------------- +// Posix Test Cases +// ---------------------------------------------------------------------- + +// All tests are inherited from the common tests in CommonTests.hpp + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Os/Posix/test/ut/PosixFileTests.cpp b/Os/Posix/test/ut/PosixFileTests.cpp index d75c5bc40f..55cc7dfc11 100644 --- a/Os/Posix/test/ut/PosixFileTests.cpp +++ b/Os/Posix/test/ut/PosixFileTests.cpp @@ -33,8 +33,8 @@ bool check_permissions(const char* path, int permission) { //! 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)]; + char full_buffer[_POSIX_PATH_MAX]; + char buffer[_POSIX_PATH_MAX - sizeof(BASE_PATH)]; // When random, select random characters if (random) { filename = buffer; @@ -47,7 +47,7 @@ std::shared_ptr get_test_filename(bool random) { } buffer[i] = 0; // Terminate random string } - (void) snprintf(full_buffer, PATH_MAX, "%s/%s", BASE_PATH, filename); + (void) snprintf(full_buffer, _POSIX_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; diff --git a/Os/Stub/CMakeLists.txt b/Os/Stub/CMakeLists.txt index 75b9271f2b..5f66a79921 100644 --- a/Os/Stub/CMakeLists.txt +++ b/Os/Stub/CMakeLists.txt @@ -22,6 +22,33 @@ set(SOURCE_FILES register_fprime_module(Os_Mutex_Stub) register_fprime_implementation(Os/Mutex Os_Mutex_Stub "${CMAKE_CURRENT_LIST_DIR}/DefaultMutex.cpp") +######## Console Stub ######## +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/Console.cpp" +) +set(HEADER_FILES + "${CMAKE_CURRENT_LIST_DIR}/Console.hpp" +) +set(MOD_DEPS) +register_fprime_module(Os_Console_Stub) +register_fprime_implementation(Os/Console Os_Console_Stub "${CMAKE_CURRENT_LIST_DIR}/DefaultConsole.cpp") + +######## FileSystem Stub ######## +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/FileSystem.cpp" +) +set(MOD_DEPS) +register_fprime_module(Os_FileSystem_Stub) +register_fprime_implementation(Os/FileSystem Os_FileSystem_Stub "${CMAKE_CURRENT_LIST_DIR}/DefaultFileSystem.cpp") + + +######## Directory Stub ######## +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/Directory.cpp" +) +set(MOD_DEPS) +register_fprime_module(Os_Directory_Stub) +register_fprime_implementation(Os/Directory Os_Directory_Stub "${CMAKE_CURRENT_LIST_DIR}/DefaultDirectory.cpp") # Remainder of file is specific to UTs if (NOT BUILD_TESTING) @@ -51,16 +78,6 @@ choose_fprime_implementation(Os/File Os_File_Test_Stub) register_fprime_ut(StubFileTest) #### Console Stub Testing #### -set(SOURCE_FILES - "${CMAKE_CURRENT_LIST_DIR}/Console.cpp" -) -set(HEADER_FILES - "${CMAKE_CURRENT_LIST_DIR}/Console.hpp" -) -set(MOD_DEPS) -register_fprime_module(Os_Console_Stub) -register_fprime_implementation(Os/Console Os_Console_Stub "${CMAKE_CURRENT_LIST_DIR}/DefaultConsole.cpp") - set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/Console.cpp" ) @@ -116,4 +133,45 @@ set(UT_MOD_DEPS STest ) choose_fprime_implementation(Os/Mutex Os_Mutex_Test_Stub) -register_fprime_ut(StubMutexTest) \ No newline at end of file +register_fprime_ut(StubMutexTest) + +#### FileSystem Stub Testing #### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/FileSystem.cpp" +) +register_fprime_module(Os_FileSystem_Test_Stub) +register_fprime_implementation(Os/FileSystem Os_FileSystem_Test_Stub "${CMAKE_CURRENT_LIST_DIR}/test/DefaultFileSystem.cpp") + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/StubFileSystemTests.cpp" + # "${CMAKE_CURRENT_LIST_DIR}/../test/ut/filesystem/CommonTests.cpp" + # "${CMAKE_CURRENT_LIST_DIR}/../test/ut/filesystem/FileSystemRules.cpp" +) +set(UT_MOD_DEPS + Os + Os_Models + STest +) +choose_fprime_implementation(Os/FileSystem Os_FileSystem_Test_Stub) +register_fprime_ut(StubFileSystemTest) + +#### Directory Stub Testing #### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/Directory.cpp" +) +register_fprime_module(Os_Directory_Test_Stub) +register_fprime_implementation(Os/Directory Os_Directory_Test_Stub "${CMAKE_CURRENT_LIST_DIR}/test/DefaultDirectory.cpp") + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/StubDirectoryTests.cpp" + # "${CMAKE_CURRENT_LIST_DIR}/../test/ut/directory/CommonTests.cpp" + # "${CMAKE_CURRENT_LIST_DIR}/../test/ut/directory/DirectoryRules.cpp" +) +set(UT_MOD_DEPS + Os + Os_Models + STest +) +choose_fprime_implementation(Os/Directory Os_Directory_Test_Stub) +register_fprime_ut(StubDirectoryTest) + diff --git a/Os/Stub/DefaultDirectory.cpp b/Os/Stub/DefaultDirectory.cpp new file mode 100644 index 0000000000..6224651bf5 --- /dev/null +++ b/Os/Stub/DefaultDirectory.cpp @@ -0,0 +1,17 @@ +// ====================================================================== +// \title Os/Stub/DefaultDirectory.cpp +// \brief sets default Os::Directory to no-op stub implementation via linker +// ====================================================================== +#include "Os/Stub/Directory.hpp" +#include "Os/Delegate.hpp" +namespace Os { + +//! \brief get a delegate for DirectoryInterface that intercepts calls for stub Directory usage +//! \param aligned_new_memory: aligned memory to fill +//! \return: pointer to delegate +DirectoryInterface *DirectoryInterface::getDelegate(HandleStorage& aligned_placement_new_memory) { + return Os::Delegate::makeDelegate( + aligned_placement_new_memory + ); +} +} diff --git a/Os/Stub/DefaultFileSystem.cpp b/Os/Stub/DefaultFileSystem.cpp new file mode 100644 index 0000000000..58a3527d23 --- /dev/null +++ b/Os/Stub/DefaultFileSystem.cpp @@ -0,0 +1,18 @@ +// ====================================================================== +// \title Os/Stub/DefaultFileSystem.cpp +// \brief sets default Os::FileSystem to no-op stub implementation via linker +// ====================================================================== +#include "Os/Stub/FileSystem.hpp" +#include "Os/Delegate.hpp" +namespace Os { + +//! \brief get a delegate for FileSystemInterface that intercepts calls for stub fileSystem usage +//! \param aligned_new_memory: aligned memory to fill +//! \param to_copy: pointer to copy-constructor input +//! \return: pointer to delegate +FileSystemInterface *FileSystemInterface::getDelegate(HandleStorage& aligned_placement_new_memory) { + return Os::Delegate::makeDelegate( + aligned_placement_new_memory + ); +} +} diff --git a/Os/Stub/Directory.cpp b/Os/Stub/Directory.cpp new file mode 100644 index 0000000000..eaa7d8a5cc --- /dev/null +++ b/Os/Stub/Directory.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// \title Os/Stub/Directory.cpp +// \brief stub implementation for Os::Directory +// ====================================================================== +#include "Os/Stub/Directory.hpp" + +namespace Os { +namespace Stub { +namespace Directory { + +StubDirectory::Status StubDirectory::open(const char* path, OpenMode mode) { + return Status::NOT_SUPPORTED; +} + +StubDirectory::Status StubDirectory::rewind() { + return Status::NOT_SUPPORTED; +} + +StubDirectory::Status StubDirectory::read(char * fileNameBuffer, FwSizeType bufSize) { + return Status::NOT_SUPPORTED; +} + +void StubDirectory::close() { + // no-op +} + +DirectoryHandle* StubDirectory::getHandle() { + return &this->m_handle; +} + +} // namespace Directory +} // namespace Stub +} // namespace Os diff --git a/Os/Stub/Directory.hpp b/Os/Stub/Directory.hpp new file mode 100644 index 0000000000..411bc85a6f --- /dev/null +++ b/Os/Stub/Directory.hpp @@ -0,0 +1,87 @@ +// ====================================================================== +// \title Os/Stub/Directory.hpp +// \brief stub definitions for Os::Directory +// ====================================================================== +#ifndef OS_STUB_DIRECTORY_HPP +#define OS_STUB_DIRECTORY_HPP + +#include "Os/Directory.hpp" +namespace Os { +namespace Stub { +namespace Directory { + +struct StubDirectoryHandle : public DirectoryHandle {}; + +//! \brief stub implementation of Os::Directory +//! +//! Stub implementation of `DirectoryInterface` for use as a delegate class handling error-only file operations. +//! +class StubDirectory : public DirectoryInterface { + public: + //! \brief constructor + StubDirectory() = default; + + //! \brief destructor + ~StubDirectory() override = default; + + //! \brief return the underlying Directory handle (implementation specific) + //! \return internal Directory handle representation + DirectoryHandle* getHandle() override; + + // ------------------------------------------------------------ + // Implementation-specific Directory member functions + // ------------------------------------------------------------ + + //! \brief Open or create a directory + //! + //! Using the path provided, this function will open or create a directory. + //! Use OpenMode::READ to open an existing directory and error if the directory is not found + //! Use OpenMode::CREATE_IF_MISSING to open a directory, creating the directory if it doesn't exist + //! Use OpenMode::CREATE_EXCLUSIVE to open a directory, creating the directory and erroring if it already exists + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to supply `mode` as a non-enumerated value. + //! + //! \param path: path of directory to open + //! \param mode: enum (READ, CREATE_IF_MISSING, CREATE_EXCLUSIVE). See notes above for more information + //! \return status of the operation + Status open(const char* path, OpenMode mode) override; + + //! \brief Check if Directory is open or not + //! \return true if Directory is open, false otherwise + bool isOpen(); + + //! \brief Rewind directory stream + //! + //! Each read operation moves the seek position forward. This function resets the seek position to the beginning. + //! + //! \return status of the operation + Status rewind() override; + + //! \brief Get next filename from directory stream + //! + //! Writes at most buffSize characters of the file name to fileNameBuffer. + //! This function skips the current directory (.) and parent directory (..) entries. + //! Returns NO_MORE_FILES if there are no more files to read from the buffer. + //! + //! It is invalid to pass `nullptr` as fileNameBuffer. + //! + //! \param fileNameBuffer: buffer to store filename + //! \param buffSize: size of fileNameBuffer + //! \return status of the operation + Status read(char * fileNameBuffer, FwSizeType buffSize) override; + + + //! \brief Close directory + void close() override; + + + private: + //! Handle for StubDirectory + StubDirectoryHandle m_handle; +}; + +} // namespace Directory +} // namespace Stub +} // namespace Os +#endif // OS_STUB_DIRECTORY_HPP diff --git a/Os/Stub/FileSystem.cpp b/Os/Stub/FileSystem.cpp new file mode 100644 index 0000000000..39fca96d51 --- /dev/null +++ b/Os/Stub/FileSystem.cpp @@ -0,0 +1,41 @@ +// ====================================================================== +// \title Os/Stub/File.cpp +// \brief stub implementation for Os::File +// ====================================================================== +#include "Os/Stub/FileSystem.hpp" + +namespace Os { +namespace Stub { +namespace FileSystem { + +StubFileSystem::Status StubFileSystem::_removeDirectory(const char* path) { + return Status::NOT_SUPPORTED; +} + +StubFileSystem::Status StubFileSystem::_removeFile(const char* path) { + return Status::NOT_SUPPORTED; +} + +StubFileSystem::Status StubFileSystem::_rename(const char* originPath, const char* destPath) { + return Status::NOT_SUPPORTED; +} + +StubFileSystem::Status StubFileSystem::_getWorkingDirectory(char* path, FwSizeType bufferSize) { + return Status::NOT_SUPPORTED; +} + +StubFileSystem::Status StubFileSystem::_changeWorkingDirectory(const char* path) { + return Status::NOT_SUPPORTED; +} + +StubFileSystem::Status StubFileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { + return Status::NOT_SUPPORTED; +} + +FileSystemHandle* StubFileSystem::getHandle() { + return &this->m_handle; +} + +} // namespace File +} // namespace Stub +} // namespace Os diff --git a/Os/Stub/FileSystem.hpp b/Os/Stub/FileSystem.hpp new file mode 100644 index 0000000000..aec7604cd5 --- /dev/null +++ b/Os/Stub/FileSystem.hpp @@ -0,0 +1,112 @@ +// ====================================================================== +// \title Os/Stub/FileSystem.hpp +// \brief stub fileSystem definitions for Os::FileSystem +// ====================================================================== +#ifndef OS_STUB_FILESYSTEM_HPP +#define OS_STUB_FILESYSTEM_HPP + +#include "Os/FileSystem.hpp" + +namespace Os { +namespace Stub { +namespace FileSystem { + +struct StubFileSystemHandle : public FileSystemHandle {}; + +//! \brief stub implementation of Os::FileSystem +//! +//! Stub implementation of `FileSystemInterface` for use as a delegate class handling error-only fileSystem operations. +//! +class StubFileSystem : public FileSystemInterface { + public: + //! \brief constructor + StubFileSystem() = default; + + //! \brief destructor + ~StubFileSystem() override = default; + + + // ------------------------------------------------------------ + // Implementation-specific FileSystem member functions + // ------------------------------------------------------------ + + //! \brief Remove a directory at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the directory to remove + //! \return Status of the operation + Status _removeDirectory(const char* path) override; + + //! \brief Remove a file at the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the file to remove + //! \return Status of the operation + Status _removeFile(const char* path) override; + + //! \brief Rename a file from source to destination + //! + //! If the rename fails due to a cross-device operation, this function should return EXDEV_ERROR + //! and moveFile can be used instead to force a copy-and-remove. + //! + //! It is invalid to pass `nullptr` as sourcePath or destPath. + //! + //! \param sourcePath The path of the source file + //! \param destPath The path of the destination file + //! \return Status of the operation + Status _rename(const char* sourcePath, const char* destPath) override; + + //! \brief Get filesystem free and total space in bytes on the filesystem containing the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path on the filesystem to query + //! \param totalBytes Reference to store the total bytes on the filesystem + //! \param freeBytes Reference to store the free bytes on the filesystem + //! \return Status of the operation + Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) override; + + //! \brief Get the current working directory + //! + //! Writes the current working directory path to the provided buffer of size bufferSize. + //! If the buffer is too small to hold the full path, the function will return BUFFER_TOO_SMALL. + //! + //! It is invalid to pass `nullptr` as the path. + //! It is invalid to pass a bufferSize of 0. + //! + //! \param path Buffer to store the current working directory path + //! \param bufferSize Size of the buffer + //! \return Status of the operation + Status _getWorkingDirectory(char* path, FwSizeType bufferSize) override; + + //! \brief Change the current working directory to the specified path + //! + //! It is invalid to pass `nullptr` as the path. + //! + //! \param path The path of the new working directory + //! \return Status of the operation + Status _changeWorkingDirectory(const char* path) override; + + + + //! \brief returns the raw fileSystem handle + //! + //! Gets the raw fileSystem 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 fileSystem handle + //! + FileSystemHandle *getHandle() override; + + +private: + //! FileSystem handle for PosixFileSystem + StubFileSystemHandle m_handle; +}; + +} // namespace FileSystem +} // namespace Stub +} // namespace Os +#endif // OS_STUB_FILESYSTEM_HPP diff --git a/Os/Stub/test/DefaultDirectory.cpp b/Os/Stub/test/DefaultDirectory.cpp new file mode 100644 index 0000000000..63ea50288d --- /dev/null +++ b/Os/Stub/test/DefaultDirectory.cpp @@ -0,0 +1,13 @@ +// ====================================================================== +// \title Os/Stub/DefaultDirectory.cpp +// \brief sets default Os::Directory to Stub implementation via linker +// ====================================================================== +// #include "Os/Directory.hpp" +#include "Os/Stub/test/Directory.hpp" +#include "Os/Delegate.hpp" + +namespace Os { +DirectoryInterface* DirectoryInterface::getDelegate(HandleStorage& aligned_new_memory) { + return Os::Delegate::makeDelegate(aligned_new_memory); +} +} diff --git a/Os/Stub/test/DefaultFileSystem.cpp b/Os/Stub/test/DefaultFileSystem.cpp new file mode 100644 index 0000000000..1d960cfa09 --- /dev/null +++ b/Os/Stub/test/DefaultFileSystem.cpp @@ -0,0 +1,19 @@ +// ====================================================================== +// \title Os/Stub/test/DefaultFileSystem.cpp +// \brief sets default Os::FileSystem to test stub implementation via linker +// ====================================================================== +#include "Os/Stub/test/FileSystem.hpp" +#include "Os/Delegate.hpp" + +namespace Os { + +//! \brief get a delegate for FileSystemInterface that intercepts calls for for stub test file usage +//! \param aligned_new_memory: aligned memory to fill +//! \param to_copy: pointer to copy-constructor input +//! \return: pointer to delegate +FileSystemInterface *FileSystemInterface::getDelegate(HandleStorage& aligned_placement_new_memory) { + return Os::Delegate::makeDelegate( + aligned_placement_new_memory + ); +} +} diff --git a/Os/Stub/test/Directory.cpp b/Os/Stub/test/Directory.cpp new file mode 100644 index 0000000000..a35fc5b009 --- /dev/null +++ b/Os/Stub/test/Directory.cpp @@ -0,0 +1,53 @@ +// ====================================================================== +// \title Os/Stub/test/Directory.cpp +// \brief implementation for TestDirectory stubs for interface testing +// ====================================================================== + +#include "Os/Stub/test/Directory.hpp" +#include "Os/Directory.hpp" + +namespace Os { +namespace Stub { +namespace Directory { +namespace Test { + +StaticData StaticData::data; + +TestDirectory::TestDirectory() { + StaticData::data.lastCalled = StaticData::LastFn::CONSTRUCT_FN; +} + +TestDirectory::~TestDirectory() { + StaticData::data.lastCalled = StaticData::LastFn::DESTRUCT_FN; +} + +TestDirectory::Status TestDirectory::open(const char* path, OpenMode mode) { + StaticData::data.lastCalled = StaticData::LastFn::OPEN_FN; + return Status::OP_OK; +} + +TestDirectory::Status TestDirectory::rewind() { + StaticData::data.lastCalled = StaticData::LastFn::REWIND_FN; + return Status::OP_OK; +} + +TestDirectory::Status TestDirectory::read(char * fileNameBuffer, FwSizeType bufSize) { + StaticData::data.lastCalled = StaticData::LastFn::READ_FN; + return Status::OP_OK; +} + +void TestDirectory::close() { + StaticData::data.lastCalled = StaticData::LastFn::CLOSE_FN; +} + + +Os::DirectoryHandle *TestDirectory::getHandle() { + StaticData::data.lastCalled = StaticData::LastFn::GET_HANDLE_FN; + return nullptr; +} + + +} +} +} +} diff --git a/Os/Stub/test/Directory.hpp b/Os/Stub/test/Directory.hpp new file mode 100644 index 0000000000..0cbb50985b --- /dev/null +++ b/Os/Stub/test/Directory.hpp @@ -0,0 +1,70 @@ +// ====================================================================== +// \title Os/Stub/test/Directory.cpp +// \brief definitions for TestDirectory stubs for interface testing +// ====================================================================== +#ifndef OS_STUB_Directory_TEST_HPP +#define OS_STUB_Directory_TEST_HPP + +#include "Os/Directory.hpp" + +namespace Os { +namespace Stub { +namespace Directory { +namespace Test { + +//! Data that supports the stubbed Directory implementation. +//!/ +struct StaticData { + //! Enumeration of last function called + enum LastFn { + NONE_FN, + CONSTRUCT_FN, + DESTRUCT_FN, + OPEN_FN, + REWIND_FN, + READ_FN, + CLOSE_FN, + GET_HANDLE_FN, + }; + StaticData() = default; + ~StaticData() = default; + + //! Last function called + LastFn lastCalled = NONE_FN; + + // Singleton data + static StaticData data; +}; + +//! Test task handle +class TestDirectoryHandle : public DirectoryHandle {}; + +//! Implementation of task +class TestDirectory : public DirectoryInterface { + public: + //! Constructor + TestDirectory(); + + //! Destructor + ~TestDirectory() override; + + + // ------------------------------------------------------------ + // Implementation-specific Directory member functions + // ------------------------------------------------------------ + Status open(const char* path, OpenMode mode) override; + Status rewind() override; + Status read(char * fileNameBuffer, FwSizeType bufSize) override; + void close() override; + + //! \brief return the underlying Directory handle (implementation specific) + //! \return internal task handle representation + DirectoryHandle* getHandle() override; + +}; + +} +} +} +} +#endif // End OS_STUB_MUTEX_TEST_HPP diff --git a/Os/Stub/test/FileSystem.cpp b/Os/Stub/test/FileSystem.cpp new file mode 100644 index 0000000000..fd0f7f6132 --- /dev/null +++ b/Os/Stub/test/FileSystem.cpp @@ -0,0 +1,63 @@ +// ====================================================================== +// \title Os/Stub/test/FileSystem.cpp +// \brief implementation for TestFileSystem stubs for interface testing +// ====================================================================== + +#include "Os/Stub/test/FileSystem.hpp" +#include "Os/FileSystem.hpp" + +namespace Os { +namespace Stub { +namespace FileSystem { +namespace Test { + +StaticData StaticData::data; + +TestFileSystem::TestFileSystem() { + StaticData::data.lastCalled = StaticData::LastFn::CONSTRUCT_FN; +} + +TestFileSystem::~TestFileSystem() { + StaticData::data.lastCalled = StaticData::LastFn::DESTRUCT_FN; +} + +TestFileSystem::Status TestFileSystem::_removeDirectory(const char* path) { + StaticData::data.lastCalled = StaticData::LastFn::REMOVE_DIR_FN; + return Status::OP_OK; +} + +TestFileSystem::Status TestFileSystem::_removeFile(const char* path) { + StaticData::data.lastCalled = StaticData::LastFn::REMOVE_FILE_FN; + return Status::OP_OK; +} + +TestFileSystem::Status TestFileSystem::_rename(const char* originPath, const char* destPath) { + StaticData::data.lastCalled = StaticData::LastFn::RENAME_FN; + return Status::OP_OK; +} + +TestFileSystem::Status TestFileSystem::_getWorkingDirectory(char* path, FwSizeType size) { + StaticData::data.lastCalled = StaticData::LastFn::GET_CWD_FN; + return Status::OP_OK; +} + +TestFileSystem::Status TestFileSystem::_changeWorkingDirectory(const char* path) { + StaticData::data.lastCalled = StaticData::LastFn::CHANGE_CWD_FN; + return Status::OP_OK; +} + +TestFileSystem::Status TestFileSystem::_getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) { + StaticData::data.lastCalled = StaticData::LastFn::GET_FREESPACE_FN; + return Status::OP_OK; +} + +Os::FileSystemHandle *TestFileSystem::getHandle() { + StaticData::data.lastCalled = StaticData::LastFn::GET_HANDLE_FN; + return nullptr; +} + + +} +} +} +} diff --git a/Os/Stub/test/FileSystem.hpp b/Os/Stub/test/FileSystem.hpp new file mode 100644 index 0000000000..881a59d53e --- /dev/null +++ b/Os/Stub/test/FileSystem.hpp @@ -0,0 +1,77 @@ +// ====================================================================== +// \title Os/Stub/test/FileSystem.cpp +// \brief definitions for TestFileSystem stubs for interface testing +// +// These classes are here to test that delegation of Os::FileSystem calls +// the selected implementation of a FileSystemInterface. +// ====================================================================== +#ifndef OS_STUB_FILESYSTEM_TEST_HPP +#define OS_STUB_FILESYSTEM_TEST_HPP + +#include "Os/FileSystem.hpp" + +namespace Os { +namespace Stub { +namespace FileSystem { +namespace Test { + +//! Data that supports the stubbed FileSystem implementation. +//!/ +struct StaticData { + //! Enumeration of last function called + //! + enum LastFn { + NONE_FN, + CONSTRUCT_FN, + DESTRUCT_FN, + CREATE_DIR_FN, + REMOVE_DIR_FN, + REMOVE_FILE_FN, + RENAME_FN, + GET_CWD_FN, + CHANGE_CWD_FN, + GET_FREESPACE_FN, + GET_HANDLE_FN, + }; + StaticData() = default; + ~StaticData() = default; + + //! Last function called + LastFn lastCalled = NONE_FN; + + Os::FileSystem::Status lastStatus = Os::FileSystem::Status::OP_OK; + + // Singleton data + static StaticData data; +}; + +//! Test task handle +class TestFileSystemHandle : public FileSystemHandle {}; + +//! Implementation of task +class TestFileSystem : public FileSystemInterface { + public: + //! Constructor + TestFileSystem(); + + //! Destructor + ~TestFileSystem() override; + + Status _removeDirectory(const char* path) override; + Status _removeFile(const char* path) override; + Status _rename(const char* originPath, const char* destPath) override; + Status _getFreeSpace(const char* path, FwSizeType& totalBytes, FwSizeType& freeBytes) override; + Status _changeWorkingDirectory(const char* path) override; + Status _getWorkingDirectory(char* path, FwSizeType size) override; + + //! \brief return the underlying FileSystem handle (implementation specific) + //! \return internal task handle representation + FileSystemHandle* getHandle() override; + +}; + +} +} +} +} +#endif // End OS_STUB_MUTEX_TEST_HPP diff --git a/Os/Stub/test/ut/StubDirectoryTests.cpp b/Os/Stub/test/ut/StubDirectoryTests.cpp new file mode 100644 index 0000000000..b46529448d --- /dev/null +++ b/Os/Stub/test/ut/StubDirectoryTests.cpp @@ -0,0 +1,78 @@ +// ====================================================================== +// \title Os/Stub/test/ut/StubDirectoryTests.cpp +// \brief stub implementation for Os::DirectoryInterface testing +// This ensures the delegation of function calls happens properly +// ====================================================================== +#include +#include "Os/test/ut/directory/CommonTests.hpp" +#include "Os/test/ut/directory/RulesHeaders.hpp" +#include "Os/Stub/test/Directory.hpp" +#include "Os/FileSystem.hpp" + +using namespace Os::Stub::Directory::Test; + + +// Basic file tests +class Interface : public ::testing::Test { +public: + //! Setup function delegating to UT setUp function + void SetUp() override { + StaticData::data = StaticData(); + } + + //! Setup function delegating to UT tearDown function + void TearDown() override { + StaticData::data = StaticData(); + } +}; + + +// Ensure that Os::Directory properly calls the implementation constructor +TEST_F(Interface, Construction) { + Os::Directory directory; + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::CONSTRUCT_FN); +} + +// Ensure that Os::Directory properly calls the implementation destructor +TEST_F(Interface, Destruction) { + delete (new Os::Directory); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DESTRUCT_FN); +} + +// Ensure that Os::Directory properly calls the implementation open() +TEST_F(Interface, Open) { + Os::Directory directory; + directory.open("/does/not/matter", Os::Directory::OpenMode::CREATE_IF_MISSING); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::OPEN_FN); +} + +// Ensure that Os::Directory properly calls the implementation rewind() +TEST_F(Interface, Rewind) { + Os::Directory directory; + directory.open("/does/not/matter", Os::Directory::OpenMode::READ); + directory.rewind(); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::REWIND_FN); +} + +// Ensure that Os::Directory properly calls the implementation open() +TEST_F(Interface, Read) { + Os::Directory directory; + directory.open("/does/not/matter", Os::Directory::OpenMode::READ); + char buffer[4]; + directory.read(buffer, 4); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::READ_FN); +} + +// Ensure that Os::Directory properly calls the implementation open() +TEST_F(Interface, Close) { + Os::Directory directory; + directory.close(); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::CLOSE_FN); +} + + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + STest::Random::seed(); + return RUN_ALL_TESTS(); +} diff --git a/Os/Stub/test/ut/StubFileSystemTests.cpp b/Os/Stub/test/ut/StubFileSystemTests.cpp new file mode 100644 index 0000000000..9b25736cf3 --- /dev/null +++ b/Os/Stub/test/ut/StubFileSystemTests.cpp @@ -0,0 +1,84 @@ +// ====================================================================== +// \title Os/Stub/test/ut/StubFileSystemTests.cpp +// \brief stub implementation for Os::FileSystemInterface testing +// This ensures the delegation of function calls happens properly +// ====================================================================== +#include +#include "Os/test/ut/filesystem/CommonTests.hpp" +#include "Os/test/ut/filesystem/RulesHeaders.hpp" +#include "Os/Stub/test/FileSystem.hpp" + +using namespace Os::Stub::FileSystem::Test; + + +// Basic file tests +class Interface : public ::testing::Test { +public: + //! Setup function delegating to UT setUp function + void SetUp() override { + StaticData::data = StaticData(); + } + + //! Setup function delegating to UT tearDown function + void TearDown() override { + StaticData::data = StaticData(); + } +}; + + +// Ensure that Os::FileSystem properly calls the implementation removeDirectory() +TEST_F(Interface, RemoveDirectory) { + Os::FileSystem::removeDirectory("/does/not/matter"); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::REMOVE_DIR_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation removeFile() +TEST_F(Interface, RemoveFile) { + Os::FileSystem::removeFile("/does/not/matter"); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::REMOVE_FILE_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation moveFile() +TEST_F(Interface, Rename) { + Os::FileSystem::rename("/does/not/matter", "/does/not/matter"); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::RENAME_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation changeWorkingDirectory() +TEST_F(Interface, GetWorkingDirectory) { + char unused[1]; + Os::FileSystem::getWorkingDirectory(unused, 1); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_CWD_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation changeWorkingDirectory() +TEST_F(Interface, ChangeWorkingDirectory) { + Os::FileSystem::changeWorkingDirectory("/does/not/matter"); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::CHANGE_CWD_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation getFreeSpace() +TEST_F(Interface, GetFreeSpace) { + FwSizeType totalBytes; + FwSizeType freeBytes; + Os::FileSystem::getFreeSpace("/does/not/matter", totalBytes, freeBytes); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_FREESPACE_FN); + ASSERT_EQ(StaticData::data.lastStatus, Os::FileSystem::Status::OP_OK); +} + +// Ensure that Os::FileSystem properly calls the implementation getHandle() +TEST_F(Interface, GetHandle) { + ASSERT_EQ(Os::FileSystem::getSingleton().getHandle(), nullptr); + ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_HANDLE_FN); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + STest::Random::seed(); + return RUN_ALL_TESTS(); +} diff --git a/Os/test/ut/OsFileSystemTest.cpp b/Os/test/ut/OsFileSystemTest.cpp deleted file mode 100644 index 4ea9aedf74..0000000000 --- a/Os/test/ut/OsFileSystemTest.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -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; - 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); - 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); - -} - -extern "C" { - void fileSystemTest(); -} - -void fileSystemTest() { - testTestFileSystem(); -} diff --git a/Os/test/ut/OsTestMain.cpp b/Os/test/ut/OsTestMain.cpp index fb29cdac4d..0c3421f1a3 100644 --- a/Os/test/ut/OsTestMain.cpp +++ b/Os/test/ut/OsTestMain.cpp @@ -11,7 +11,6 @@ extern "C" { void qtest_performance(); void qtest_concurrent(); void intervalTimerTest(); - void fileSystemTest(); void validateFileTest(const char* filename); void systemResourcesTest(); void mutexBasicLockableTest(); @@ -44,9 +43,6 @@ TEST(Nominal, QTestConcurrentTest) { TEST(Nominal, DISABLED_IntervalTimerTest) { intervalTimerTest(); } -TEST(Nominal, FileSystemTest) { - fileSystemTest(); -} TEST(Nominal, ValidateFileTest) { validateFileTest(filename); } diff --git a/Os/test/ut/directory/CommonTests.cpp b/Os/test/ut/directory/CommonTests.cpp new file mode 100644 index 0000000000..a68a1850ae --- /dev/null +++ b/Os/test/ut/directory/CommonTests.cpp @@ -0,0 +1,186 @@ +// ====================================================================== +// \title Os/test/ut/directory/CommonTests.cpp +// \brief common test implementations +// ====================================================================== +#include "Os/test/ut/directory/CommonTests.hpp" + +#include // for setup +#include "STest/Pick/Pick.hpp" + + +std::unique_ptr get_tester_implementation() { + return std::unique_ptr(new Os::Test::Directory::Tester()); +} + +Functionality::Functionality() : tester(get_tester_implementation()) {} + + +//! Create a directory with a number of files in it +void Functionality::SetUp() { + Os::Test::Directory::setUp(this->tester.get()); +} + +void Functionality::TearDown() { + Os::Test::Directory::tearDown(this->tester.get()); +} + +// ---------------------------------------------------------------------- +// Test Cases +// ---------------------------------------------------------------------- + + +// Open directory and check it is open +TEST_F(Functionality, OpenIsOpen) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); +} + +// Open directory and check it is open +TEST_F(Functionality, OpenExclusive) { + Os::Test::Directory::Tester::OpenAlreadyExistsError open_existing_rule; + open_existing_rule.apply(*tester); +} + +// Do not open directory and check it is not open +TEST_F(Functionality, IsNotOpen) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsNotOpen is_not_open_rule; + is_not_open_rule.apply(*tester); + open_rule.apply(*tester); + close_rule.apply(*tester); + is_not_open_rule.apply(*tester); +} + +// Read file from directory +TEST_F(Functionality, ReadOneFile) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::ReadOneFile read_one_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); + read_one_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Read file from directory using read(StringBase&) overload +TEST_F(Functionality, ReadOneFileString) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::ReadOneFileString read_str_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); + read_str_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Read file from directory +TEST_F(Functionality, ReadRewindRead) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::ReadOneFile read_rule; + Os::Test::Directory::Tester::Rewind rewind_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); + read_rule.apply(*tester); + rewind_rule.apply(*tester); + read_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Read file from directory +TEST_F(Functionality, GetFileCount) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::GetFileCount file_count_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); + file_count_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Read file from directory +TEST_F(Functionality, ReadAllFiles) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::ReadAllFiles read_all_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + open_rule.apply(*tester); + is_open_rule.apply(*tester); + read_all_rule.apply(*tester); + close_rule.apply(*tester); +} + +// Read a closed directory and expect an error +TEST_F(Functionality, ReadClosedDirectory) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::ReadWithoutOpen read_closed_rule; + open_rule.apply(*tester); + close_rule.apply(*tester); + read_closed_rule.apply(*tester); +} + +// Rewind a closed directory and expect an error +TEST_F(Functionality, RewindClosedDirectory) { + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::RewindWithoutOpen rewind_closed_rule; + open_rule.apply(*tester); + close_rule.apply(*tester); + rewind_closed_rule.apply(*tester); +} + +TEST_F(Functionality, RandomizedTesting) { + // Enumerate all rules and construct an instance of each + Os::Test::Directory::Tester::Open open_rule; + Os::Test::Directory::Tester::Close close_rule; + Os::Test::Directory::Tester::IsOpen is_open_rule; + Os::Test::Directory::Tester::IsNotOpen is_not_open_rule; + Os::Test::Directory::Tester::ReadOneFile read_rule; + Os::Test::Directory::Tester::ReadOneFileString read_str_rule; + Os::Test::Directory::Tester::Rewind rewind_rule; + Os::Test::Directory::Tester::ReadAllFiles read_all_rule; + Os::Test::Directory::Tester::GetFileCount file_count_rule; + Os::Test::Directory::Tester::ReadWithoutOpen read_closed_rule; + Os::Test::Directory::Tester::RewindWithoutOpen rewind_closed_rule; + + // Place these rules into a list of rules + STest::Rule* rules[] = { + &open_rule, + &close_rule, + &is_open_rule, + &is_not_open_rule, + &read_rule, + &read_str_rule, + &rewind_rule, + &read_all_rule, + &file_count_rule, + &read_closed_rule, + &rewind_closed_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, + 1000 + ); + // Run! + const U32 numSteps = bounded.run(*tester); + printf("Ran %u steps with %zu files in test directory.\n", numSteps, tester->m_filenames.size()); + +} diff --git a/Os/test/ut/directory/CommonTests.hpp b/Os/test/ut/directory/CommonTests.hpp new file mode 100644 index 0000000000..fc3462563e --- /dev/null +++ b/Os/test/ut/directory/CommonTests.hpp @@ -0,0 +1,55 @@ +// ====================================================================== +// \title Os/test/ut/directory/CommonTests.hpp +// \brief GoogleTest fixture definitions used in common Directory testing +// ====================================================================== +#ifndef OS_TEST_UT_COMMON_DIRECTORY_TESTS_HPP +#define OS_TEST_UT_COMMON_DIRECTORY_TESTS_HPP + +#include +#include +#include + +// ---------------------------------------------------------------------- +//! Notes on CommonTests.cpp reusability +// +//! In order to reuse these tests, the implementor of an OSAL implementation shall +//! provide a setUp and tearDown implementation in the Os::Test::Directory namespace +//! An example is in Os/Posix/test/ut/PosixDirectoryTests.cpp +//! The setUp function shall create a test directory and populate it with files +//! The directory and files shall be tracked in the tester object with: +//! - The path of the test directory shall be assigned to the tester->m_path member +//! - The name of each file shall be pushed in tester->m_filenames vector +//! There are no requirement on the number of the files, a random number >=0 is recommended +//! The tearDown function shall remove the test directory and all files in it +// ---------------------------------------------------------------------- + +namespace Os { +namespace Test { +namespace Directory { + +//! Set up function as defined by the unit test implementor +void setUp(Os::Test::Directory::Tester* tester); + +//! Tear down function as defined by the unit test implementor +void tearDown(Os::Test::Directory::Tester* tester); + +} // namespace Directory +} // namespace Test +} // namespace Os + +class Functionality : public ::testing::Test { + public: + //! Constructor + Functionality(); + + //! SetUp test fixture + void SetUp() override; + + //! TearDown test fixture for safe destruction + void TearDown() override; + + //! Tester/state implementation + std::unique_ptr tester; +}; + +#endif // OS_TEST_UT_COMMON_DIRECTORY_TESTS_HPP diff --git a/Os/test/ut/directory/DirectoryRules.cpp b/Os/test/ut/directory/DirectoryRules.cpp new file mode 100644 index 0000000000..b2b1b3cbb2 --- /dev/null +++ b/Os/test/ut/directory/DirectoryRules.cpp @@ -0,0 +1,234 @@ +// ====================================================================== +// \title Os/test/ut/directory/DirectoryRules.cpp +// \brief rule implementations for common testing of directory +// ====================================================================== + +#include "RulesHeaders.hpp" +#include "DirectoryRules.hpp" +#include "STest/Pick/Pick.hpp" +#include + + +// ------------------------------------------------------------------------------------------------------ +// Rule: Open -> Open a directory +// ------------------------------------------------------------------------------------------------------ + +Os::Test::Directory::Tester::Open::Open() : + STest::Rule("Open") {} + +bool Os::Test::Directory::Tester::Open::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state != Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::Open::action(Os::Test::Directory::Tester &state) { + Os::Directory::OpenMode mode = STest::Pick::lowerUpper(0, 1) == 1 ? Os::Directory::READ : Os::Directory::CREATE_IF_MISSING; + Os::Directory::Status status = state.m_directory.open(state.m_path.c_str(), mode); + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + state.m_state = Os::Test::Directory::Tester::DirectoryState::OPEN; + state.m_seek_position = 0; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenAlreadyExistsError -> Open an already existing directory with CREATE_EXCLUSIVE and error +// ------------------------------------------------------------------------------------------------------ + +Os::Test::Directory::Tester::OpenAlreadyExistsError::OpenAlreadyExistsError() : + STest::Rule("OpenAlreadyExistsError") {} + +bool Os::Test::Directory::Tester::OpenAlreadyExistsError::precondition(const Os::Test::Directory::Tester &state) { + return true; +} + +void Os::Test::Directory::Tester::OpenAlreadyExistsError::action(Os::Test::Directory::Tester &state) { + Os::Directory new_directory; + Os::Directory::Status status = new_directory.open(state.m_path.c_str(), Os::Directory::OpenMode::CREATE_EXCLUSIVE); + ASSERT_EQ(status, Os::Directory::Status::ALREADY_EXISTS); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Close -> Close a directory +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::Close::Close() : + STest::Rule("Close") {} + +bool Os::Test::Directory::Tester::Close::precondition(const Os::Test::Directory::Tester &state) { + return true; +} + +void Os::Test::Directory::Tester::Close::action(Os::Test::Directory::Tester &state) { + state.m_directory.close(); + ASSERT_FALSE(state.m_directory.isOpen()); + state.m_state = Os::Test::Directory::Tester::DirectoryState::CLOSED; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: IsOpen -> Check if a directory is open +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::IsOpen::IsOpen() : + STest::Rule("IsOpen") {} + +bool Os::Test::Directory::Tester::IsOpen::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::IsOpen::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: IsNotOpen -> Check if a directory is not open +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::IsNotOpen::IsNotOpen() : + STest::Rule("IsNotOpen") {} + +bool Os::Test::Directory::Tester::IsNotOpen::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state != Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::IsNotOpen::action(Os::Test::Directory::Tester &state) { + ASSERT_FALSE(state.m_directory.isOpen()); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadOneFile -> Read one file from a directory and assert name +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::ReadOneFile::ReadOneFile() : + STest::Rule("ReadOneFile") {} + +bool Os::Test::Directory::Tester::ReadOneFile::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::ReadOneFile::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); + char filename[100]; + Os::Directory::Status status = state.m_directory.read(filename, 100); + // If seek is at the end of the directory, expect NO_MORE_FILES - otherwise expect normal read and valid filename + if (state.m_seek_position < static_cast(state.m_filenames.size())) { + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + ASSERT_TRUE(state.is_valid_filename(std::string(filename))); + state.m_seek_position++; + } else { + ASSERT_EQ(status, Os::Directory::Status::NO_MORE_FILES); + } +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadOneFileString -> Read one file from a directory and assert name +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::ReadOneFileString::ReadOneFileString() : + STest::Rule("ReadOneFileString") {} + +bool Os::Test::Directory::Tester::ReadOneFileString::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::ReadOneFileString::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); + Fw::String filename; + Os::Directory::Status status = state.m_directory.read(filename); + // If seek is at the end of the directory, expect NO_MORE_FILES - otherwise expect normal read and valid filename + if (state.m_seek_position < static_cast(state.m_filenames.size())) { + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + ASSERT_TRUE(state.is_valid_filename(std::string(filename.toChar()))); + state.m_seek_position++; + } else { + ASSERT_EQ(status, Os::Directory::Status::NO_MORE_FILES); + } +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: Rewind -> Rewind a directory +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::Rewind::Rewind() : + STest::Rule("Rewind") {} + +bool Os::Test::Directory::Tester::Rewind::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::Rewind::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); + Os::Directory::Status status = state.m_directory.rewind(); + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + state.m_seek_position = 0; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFileCount -> Get the file count in a directory +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::GetFileCount::GetFileCount() : + STest::Rule("GetFileCount") {} + +bool Os::Test::Directory::Tester::GetFileCount::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::GetFileCount::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); + FwSizeType fileCount; + Os::Directory::Status status = state.m_directory.getFileCount(fileCount); + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + ASSERT_EQ(fileCount, state.m_filenames.size()); + + // NOTE: getFileCount reads in order to count + // therefore it plays with seek_position. Should it be called out in interface? Or should Posix implementation change? + state.m_seek_position = 0; // Reset seek position when getFileCount is called +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadAllFiles -> Read all files in a directory +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::ReadAllFiles::ReadAllFiles() : + STest::Rule("ReadAllFiles") {} + +bool Os::Test::Directory::Tester::ReadAllFiles::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state == Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::ReadAllFiles::action(Os::Test::Directory::Tester &state) { + ASSERT_TRUE(state.m_directory.isOpen()); + const FwSizeType arraySize = FW_MAX(state.m_filenames.size(), 1); // .size() can be 0 during testing so ensure at least 1 + Fw::String outArray[arraySize]; + FwSizeType outFileCount = 0; + Os::Directory::Status status = state.m_directory.readDirectory(outArray, arraySize, outFileCount); + ASSERT_EQ(status, Os::Directory::Status::OP_OK); + // Number of files read should be the number of files in the directory minus the original seek position + ASSERT_EQ(outFileCount, state.m_filenames.size()); + for (FwSizeType i = 0; i < outFileCount; i++) { + ASSERT_TRUE(state.is_valid_filename(std::string(outArray[i].toChar()))); + } + // readDirectory resets the seek position to the end + state.m_seek_position = 0; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadWithoutOpen -> Read a closed directory and expect Status::NOT_OPENED +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::ReadWithoutOpen::ReadWithoutOpen() : + STest::Rule("ReadWithoutOpen") {} + +bool Os::Test::Directory::Tester::ReadWithoutOpen::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state != Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::ReadWithoutOpen::action(Os::Test::Directory::Tester &state) { + ASSERT_FALSE(state.m_directory.isOpen()); + char unused[4]; + ASSERT_EQ(state.m_directory.read(unused, 4), Os::Directory::Status::NOT_OPENED); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: RewindWithoutOpen -> Rewind a closed directory and expect Status::NOT_OPENED +// ------------------------------------------------------------------------------------------------------ +Os::Test::Directory::Tester::RewindWithoutOpen::RewindWithoutOpen() : + STest::Rule("RewindWithoutOpen") {} + +bool Os::Test::Directory::Tester::RewindWithoutOpen::precondition(const Os::Test::Directory::Tester &state) { + return state.m_state != Os::Test::Directory::Tester::DirectoryState::OPEN; +} + +void Os::Test::Directory::Tester::RewindWithoutOpen::action(Os::Test::Directory::Tester &state) { + ASSERT_FALSE(state.m_directory.isOpen()); + ASSERT_EQ(state.m_directory.rewind(), Os::Directory::Status::NOT_OPENED); +} diff --git a/Os/test/ut/directory/DirectoryRules.hpp b/Os/test/ut/directory/DirectoryRules.hpp new file mode 100644 index 0000000000..d3a8c8858e --- /dev/null +++ b/Os/test/ut/directory/DirectoryRules.hpp @@ -0,0 +1,123 @@ +// ====================================================================== +// \title Os/test/ut/directory/DirectoryRules.hpp +// \brief rule definitions for common testing of directory +// ====================================================================== +// Stripped when compiled, here for IDEs +#include "RulesHeaders.hpp" + +// ------------------------------------------------------------------------------------------------------ +// Rule: Open a directory +// ------------------------------------------------------------------------------------------------------ +struct Open : public STest::Rule { + //! Constructor + Open(); + + //! Precondition + bool precondition(const Os::Test::Directory::Tester& state //!< The test state + ); + + //! Action + void action(Os::Test::Directory::Tester& state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: OpenAlreadyExistsError +// ------------------------------------------------------------------------------------------------------ +struct OpenAlreadyExistsError : public STest::Rule { + OpenAlreadyExistsError(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Close a directory +// ------------------------------------------------------------------------------------------------------ +struct Close : public STest::Rule { + Close(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Check isOpen return true on an open directory +// Note: maybe not needed if we assert +// ------------------------------------------------------------------------------------------------------ +struct IsOpen : public STest::Rule { + IsOpen(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Check isOpen return false on non-open directory +// Note: maybe not needed if we assert +// ------------------------------------------------------------------------------------------------------ +struct IsNotOpen : public STest::Rule { + IsNotOpen(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read the first file in a directory that has been opened +// ------------------------------------------------------------------------------------------------------ +struct ReadOneFile : public STest::Rule { + ReadOneFile(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read the first file in a directory that has been opened, using read(StringBase&) implementation +// ------------------------------------------------------------------------------------------------------ +struct ReadOneFileString : public STest::Rule { + ReadOneFileString(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Rewind a directory +// ------------------------------------------------------------------------------------------------------ +struct Rewind : public STest::Rule { + Rewind(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFileCount of a directory +// ------------------------------------------------------------------------------------------------------ +struct GetFileCount : public STest::Rule { + GetFileCount(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: ReadAllFiles from a directory +// ------------------------------------------------------------------------------------------------------ +struct ReadAllFiles : public STest::Rule { + ReadAllFiles(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Read a closed directory and expect an error +// ------------------------------------------------------------------------------------------------------ +struct ReadWithoutOpen : public STest::Rule { + ReadWithoutOpen(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: Rewind a closed directory and expect an error +// ------------------------------------------------------------------------------------------------------ +struct RewindWithoutOpen : public STest::Rule { + RewindWithoutOpen(); + bool precondition(const Os::Test::Directory::Tester& state); + void action(Os::Test::Directory::Tester& state); +}; diff --git a/Os/test/ut/directory/RulesHeaders.hpp b/Os/test/ut/directory/RulesHeaders.hpp new file mode 100644 index 0000000000..af2d18f3d9 --- /dev/null +++ b/Os/test/ut/directory/RulesHeaders.hpp @@ -0,0 +1,63 @@ +// ====================================================================== +// \title Os/test/ut/directory/RulesHeaders.hpp +// \brief rule definitions for common testing +// ====================================================================== + +#ifndef __RULES_HEADERS__ +#define __RULES_HEADERS__ + +#include +#include "Os/Directory.hpp" +#include "STest/Rule/Rule.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" +#include "STest/Scenario/Scenario.hpp" + +#include // for std::find + + +namespace Os { +namespace Test { +namespace Directory { + +struct Tester { + //! State representation of a Directory. + //! + enum DirectoryState { + UNINITIALIZED, //!< Directory is uninitialized + OPEN, //!< Directory is open + CLOSED //!< Directory is closed + }; + + // Constructors that ensures the directory is always valid + Tester() = default; + + // Destructor must be virtual + virtual ~Tester() = default; + + //! Directory under test + Os::Directory m_directory; + + //! Tracks the directory state, for testing purposes + DirectoryState m_state = UNINITIALIZED; + //! Tracks the currently opened path, for testing purposes + std::string m_path; + //! Tracks the list of filenames created for the tested directory, for testing purposes + std::vector m_filenames; + //! Tracks the seek position of directory, for testing purposes + FwIndexType m_seek_position = 0; + + + //! \brief Check if filename is in the list of test m_filenames created for the tested directory + bool is_valid_filename(const std::string& filename) const { + return std::find(m_filenames.cbegin(), m_filenames.cend(), filename) != m_filenames.cend(); + } + +// Do NOT alter, adds rules to Tester as inner classes +#include "DirectoryRules.hpp" +}; + +} // namespace Directory +} // namespace Test +} // namespace Os +#endif // __RULES_HEADERS__ diff --git a/Os/test/ut/file/FileRules.cpp b/Os/test/ut/file/FileRules.cpp index 868c4053f0..39b0399b61 100644 --- a/Os/test/ut/file/FileRules.cpp +++ b/Os/test/ut/file/FileRules.cpp @@ -2,7 +2,7 @@ // \title Os/test/ut/file/MyRules.cpp // \brief rule implementations for common testing // ====================================================================== - +#include #include "RulesHeaders.hpp" #include "STest/Pick/Pick.hpp" extern "C" { @@ -20,8 +20,8 @@ Os::File::Status Os::Test::File::Tester::shadow_open(const std::string &path, Os 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; } @@ -127,6 +127,8 @@ void Os::Test::File::Tester::assert_valid_mode_status(Os::File::Status &status) void Os::Test::File::Tester::assert_file_consistent() { // Ensure file mode ASSERT_EQ(this->m_mode, this->m_file.m_mode); + // Ensure CRC match + ASSERT_EQ(this->m_file.m_crc, this->m_independent_crc); if (this->m_file.m_path == nullptr) { ASSERT_EQ(this->m_current_path, std::string("")); } else { @@ -229,7 +231,7 @@ void Os::Test::File::Tester::assert_file_seek(const FwSignedSizeType original_po 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) { + if (expected_offset >= 0) { ASSERT_EQ(new_position, expected_offset); } else { ASSERT_EQ(new_position, original_position); @@ -256,6 +258,7 @@ bool Os::Test::File::Tester::OpenBaseRule::precondition(const Os::Test::File::Te void Os::Test::File::Tester::OpenBaseRule::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); // Initial variables used for this test std::shared_ptr filename = state.get_filename(this->m_random); @@ -344,6 +347,7 @@ bool Os::Test::File::Tester::CloseFile::precondition(const Os::Test::File::Teste void Os::Test::File::Tester::CloseFile::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); // Make sure test state and file state synchronized state.assert_file_consistent(); state.assert_file_opened(state.m_current_path); @@ -375,6 +379,7 @@ bool Os::Test::File::Tester::Read::precondition( void Os::Test::File::Tester::Read::action( Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[FILE_DATA_MAXIMUM]; state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); @@ -411,6 +416,7 @@ bool Os::Test::File::Tester::Write::precondition( void Os::Test::File::Tester::Write::action( Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[FILE_DATA_MAXIMUM]; state.assert_file_consistent(); FwSignedSizeType size_desired = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); @@ -448,6 +454,7 @@ bool Os::Test::File::Tester::Seek::precondition( void Os::Test::File::Tester::Seek::action( Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); FwSignedSizeType seek_offset = 0; state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); @@ -486,10 +493,11 @@ bool Os::Test::File::Tester::Preallocate::precondition( void Os::Test::File::Tester::Preallocate::action( Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); 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)); + FwSignedSizeType length = static_cast(STest::Pick::lowerUpper(1, 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); @@ -519,6 +527,7 @@ bool Os::Test::File::Tester::Flush::precondition( void Os::Test::File::Tester::Flush::action( Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); Os::File::Status status = state.m_file.flush(); @@ -549,6 +558,7 @@ bool Os::Test::File::Tester::OpenInvalidModes::precondition(const Os::Test::File void Os::Test::File::Tester::OpenInvalidModes::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); // Check initial file state @@ -580,6 +590,7 @@ bool Os::Test::File::Tester::PreallocateWithoutOpen::precondition( void Os::Test::File::Tester::PreallocateWithoutOpen::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); // Check initial file state state.assert_file_closed(); @@ -606,6 +617,7 @@ bool Os::Test::File::Tester::SeekWithoutOpen::precondition(const Os::Test::File: void Os::Test::File::Tester::SeekWithoutOpen::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); // Check initial file state state.assert_file_closed(); @@ -632,6 +644,7 @@ bool Os::Test::File::Tester::SeekInvalidSize::precondition(const Os::Test::File: void Os::Test::File::Tester::SeekInvalidSize::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); // Open file of given filename @@ -662,6 +675,7 @@ bool Os::Test::File::Tester::FlushInvalidModes::precondition(const Os::Test::Fil void Os::Test::File::Tester::FlushInvalidModes::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); 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); @@ -689,6 +703,7 @@ bool Os::Test::File::Tester::ReadInvalidModes::precondition(const Os::Test::File void Os::Test::File::Tester::ReadInvalidModes::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[10]; FwSignedSizeType size = sizeof buffer; state.assert_file_consistent(); @@ -720,6 +735,7 @@ bool Os::Test::File::Tester::WriteInvalidModes::precondition(const Os::Test::Fil void Os::Test::File::Tester::WriteInvalidModes::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[10]; FwSignedSizeType size = sizeof buffer; state.assert_file_consistent(); @@ -756,6 +772,7 @@ Os::Test::File::Tester::OpenIllegalPath::OpenIllegalPath() : Os::Test::File::Tes void Os::Test::File::Tester::OpenIllegalPath::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); Os::File::Mode random_mode = static_cast(STest::Pick::lowerUpper(Os::File::Mode::OPEN_READ, @@ -777,6 +794,7 @@ Os::Test::File::Tester::OpenIllegalMode::OpenIllegalMode() : Os::Test::File::Tes void Os::Test::File::Tester::OpenIllegalMode::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); std::shared_ptr random_filename = state.get_filename(true); U32 mode = STest::Pick::lowerUpper(0, 1); @@ -799,6 +817,7 @@ Os::Test::File::Tester::PreallocateIllegalOffset::PreallocateIllegalOffset() void Os::Test::File::Tester::PreallocateIllegalOffset::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FwSignedSizeType length = static_cast(STest::Pick::any()); FwSignedSizeType invalid_offset = @@ -818,6 +837,7 @@ Os::Test::File::Tester::PreallocateIllegalLength::PreallocateIllegalLength() void Os::Test::File::Tester::PreallocateIllegalLength::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FwSignedSizeType offset = static_cast(STest::Pick::any()); FwSignedSizeType invalid_length = @@ -836,6 +856,7 @@ Os::Test::File::Tester::SeekIllegal::SeekIllegal() : Os::Test::File::Tester::Ass void Os::Test::File::Tester::SeekIllegal::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); 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(); @@ -851,6 +872,7 @@ Os::Test::File::Tester::ReadIllegalBuffer::ReadIllegalBuffer() void Os::Test::File::Tester::ReadIllegalBuffer::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FwSignedSizeType size = static_cast(STest::Pick::any()); bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); @@ -869,6 +891,7 @@ Os::Test::File::Tester::ReadIllegalSize::ReadIllegalSize() : Os::Test::File::Tes void Os::Test::File::Tester::ReadIllegalSize::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[10] = {}; state.assert_file_consistent(); FwSignedSizeType invalid_size = -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); @@ -890,6 +913,7 @@ Os::Test::File::Tester::WriteIllegalBuffer::WriteIllegalBuffer() void Os::Test::File::Tester::WriteIllegalBuffer::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FwSignedSizeType size = static_cast(STest::Pick::any()); bool random_wait = static_cast(STest::Pick::lowerUpper(0, 1)); @@ -908,6 +932,7 @@ Os::Test::File::Tester::WriteIllegalSize::WriteIllegalSize() : Os::Test::File::T void Os::Test::File::Tester::WriteIllegalSize::action(Os::Test::File::Tester &state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U8 buffer[10] = {}; state.assert_file_consistent(); FwSignedSizeType invalid_size = -1 * static_cast(STest::Pick::lowerUpper(0, std::numeric_limits::max())); @@ -935,6 +960,7 @@ bool Os::Test::File::Tester::CopyAssignment::precondition(const Os::Test::File:: void Os::Test::File::Tester::CopyAssignment::action(Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); Os::File temp = state.m_file; state.assert_file_consistent(); // Prevents optimization @@ -959,6 +985,7 @@ bool Os::Test::File::Tester::CopyConstruction::precondition(const Os::Test::File void Os::Test::File::Tester::CopyConstruction::action(Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); Os::File temp(state.m_file); state.assert_file_consistent(); // Interim check to ensure original file did not change @@ -984,6 +1011,7 @@ bool Os::Test::File::Tester::FullCrc::precondition( void Os::Test::File::Tester::FullCrc::action( Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U32 crc = 1; U32 shadow_crc = 2; state.assert_file_consistent(); @@ -1013,6 +1041,7 @@ bool Os::Test::File::Tester::IncrementalCrc::precondition( void Os::Test::File::Tester::IncrementalCrc::action( Os::Test::File::Tester& state //!< The test state ){ + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FwSignedSizeType size_desired = static_cast(STest::Pick::lowerUpper(0, FW_FILE_CHUNK_SIZE)); FwSignedSizeType shadow_size = size_desired; @@ -1041,10 +1070,11 @@ bool Os::Test::File::Tester::FinalizeCrc::precondition( void Os::Test::File::Tester::FinalizeCrc::action( Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); U32 crc = 1; U32 shadow_crc = 2; state.assert_file_consistent(); - Os::File::Status status = state.m_file.finalizeCrc(crc); + 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); @@ -1069,6 +1099,7 @@ bool Os::Test::File::Tester::FullCrcInvalidModes::precondition( void Os::Test::File::Tester::FullCrcInvalidModes::action( Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); ASSERT_TRUE(Os::File::Mode::OPEN_READ != state.m_file.m_mode); @@ -1102,6 +1133,7 @@ bool Os::Test::File::Tester::IncrementalCrcInvalidModes::precondition( void Os::Test::File::Tester::IncrementalCrcInvalidModes::action( Os::Test::File::Tester& state //!< The test state ) { + printf("--> Rule: %s \n", this->getName()); state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); ASSERT_TRUE(Os::File::Mode::OPEN_READ != state.m_file.m_mode); diff --git a/Os/test/ut/file/SyntheticFileSystem.cpp b/Os/test/ut/file/SyntheticFileSystem.cpp index b81f7c6eae..5bcb59cbf4 100644 --- a/Os/test/ut/file/SyntheticFileSystem.cpp +++ b/Os/test/ut/file/SyntheticFileSystem.cpp @@ -191,7 +191,7 @@ Os::File::Status SyntheticFile::seek(const FwSignedSizeType offset, const SeekTy status = Os::File::Status::NOT_OPENED; } else { FwSignedSizeType new_offset = (absolute) ? offset : (offset + this->m_data->m_pointer); - if (new_offset > 0) { + if (new_offset >= 0) { this->m_data->m_pointer = new_offset; } else { status = Os::File::Status::INVALID_ARGUMENT; diff --git a/Os/test/ut/filesystem/CommonTests.cpp b/Os/test/ut/filesystem/CommonTests.cpp new file mode 100644 index 0000000000..5a2eae6401 --- /dev/null +++ b/Os/test/ut/filesystem/CommonTests.cpp @@ -0,0 +1,203 @@ +// ====================================================================== +// \title Os/test/ut/filesystem/CommonTests.cpp +// \brief common test implementations +// ====================================================================== +#include "Os/test/ut/filesystem/CommonTests.hpp" + +#include + + + +std::unique_ptr get_tester_implementation() { + return std::unique_ptr(new Os::Test::FileSystem::Tester()); +} + +Functionality::Functionality() : tester(get_tester_implementation()) {} + +void Functionality::SetUp() { + using namespace Os::Test::FileSystem; + const FwSizeType NUMBER_TEST_FILES = 30; + + // Lambda function to generate a random alphanumeric string + // used to populate the contents of the test files + // If need to debug, it can be useful to manually set strings + // of known values in this->m_test_files instead + auto generate_random_string = []() -> std::string { + static const char alphanums[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + FwIndexType len = STest::Pick::lowerUpper(1, 4); + std::string result; + result.reserve(len); + for (FwIndexType i = 0; i < len; ++i) { + result += alphanums[STest::Pick::lowerUpper(0, sizeof(alphanums) - 2)]; + } + return result; + }; + + // Set up test state - create a directory structure with files + std::string root_dir = "filesystem_test_directory"; + tester->m_test_dirs = { + TestDirectory(root_dir), + TestDirectory(root_dir + "/sub_dir_1"), + TestDirectory(root_dir + "/sub_dir_2") + }; + + for (FwSizeType i = 0; i < NUMBER_TEST_FILES; ++i) { + std::string path = root_dir + "/test_file_" + std::to_string(i); + tester->m_test_files.push_back(TestFile(path, generate_random_string())); + } + + // All of the above is in the tester state, i.e. in memory + // The below initializes the filesystem with the same structure, on disk + tester->write_test_state_to_disk(); +} + +void Functionality::TearDown() { + tester->purge_test_state_from_disk(); +} + +// ---------------------------------------------------------------------- +// Test Cases +// ---------------------------------------------------------------------- + +// Existing paths exists, non-existing paths do not exist +TEST_F(Functionality, Exists) { + Os::Test::FileSystem::Tester::DirectoryExists dir_exist_rule; + Os::Test::FileSystem::Tester::FileExists file_exist_rule; + Os::Test::FileSystem::Tester::PathNotExists no_exist_rule; + dir_exist_rule.apply(*tester); + file_exist_rule.apply(*tester); + no_exist_rule.apply(*tester); +} + +// RemoveFile removes a file +TEST_F(Functionality, RemoveFile) { + Os::Test::FileSystem::Tester::RemoveFile rm_file_rule; + rm_file_rule.apply(*tester); +} + +// RemoveDirectory removes a directory +TEST_F(Functionality, RemoveDirectory) { + Os::Test::FileSystem::Tester::RemoveDirectory rm_dir_rule; + rm_dir_rule.apply(*tester); +} + +// TouchFile touches a file +TEST_F(Functionality, TouchFile) { + Os::Test::FileSystem::Tester::TouchFile touch_rule; + touch_rule.apply(*tester); +} + +// CreateDirectory +TEST_F(Functionality, CreateDirectory) { + Os::Test::FileSystem::Tester::CreateDirectory create_dir_rule; + create_dir_rule.apply(*tester); +} + +// MoveFile +TEST_F(Functionality, MoveFile) { + Os::Test::FileSystem::Tester::MoveFile move_rule; + move_rule.apply(*tester); +} + +// RenameFile +TEST_F(Functionality, RenameFile) { + Os::Test::FileSystem::Tester::RenameFile rename_rule; + rename_rule.apply(*tester); +} + +// CopyFile +TEST_F(Functionality, CopyFile) { + Os::Test::FileSystem::Tester::CopyFile move_rule; + move_rule.apply(*tester); +} + +// AppendFile +TEST_F(Functionality, AppendFile) { + Os::Test::FileSystem::Tester::AppendFile append_rule; + append_rule.apply(*tester); +} + +// AppendToNewFile +TEST_F(Functionality, AppendToNewFile) { + Os::Test::FileSystem::Tester::AppendToNewFile append_new_rule; + append_new_rule.apply(*tester); +} + +// GetFileSize +TEST_F(Functionality, GetFileSize) { + Os::Test::FileSystem::Tester::GetFileSize get_size_rule; + get_size_rule.apply(*tester); +} + +// GetFreeSpace +TEST_F(Functionality, GetFreeSpace) { + Os::Test::FileSystem::Tester::GetFreeSpace free_space_rule; + free_space_rule.apply(*tester); +} + +// // Test both get and set working directory +TEST_F(Functionality, GetSetWorkingDirectory) { + Os::Test::FileSystem::Tester::GetSetWorkingDirectory change_cwd_rule; + change_cwd_rule.apply(*tester); +} + +// Randomized testing +TEST_F(Functionality, RandomizedTesting) { + // Enumerate all rules and construct an instance of each + + Os::Test::FileSystem::Tester::DirectoryExists directory_exists_rule; + Os::Test::FileSystem::Tester::FileExists file_exists_rule; + Os::Test::FileSystem::Tester::PathNotExists not_exists_rule; + Os::Test::FileSystem::Tester::RemoveFile remove_rule; + Os::Test::FileSystem::Tester::RemoveDirectory remove_directory_rule; + Os::Test::FileSystem::Tester::TouchFile touch_rule; + Os::Test::FileSystem::Tester::CreateDirectory create_directory_rule; + Os::Test::FileSystem::Tester::MoveFile move_rule; + Os::Test::FileSystem::Tester::RenameFile rename_rule; + Os::Test::FileSystem::Tester::CopyFile copyfile_rule; + Os::Test::FileSystem::Tester::AppendFile append_rule; + Os::Test::FileSystem::Tester::AppendToNewFile append_new_rule; + Os::Test::FileSystem::Tester::GetFileSize file_size_rule; + Os::Test::FileSystem::Tester::GetFreeSpace free_space_rule; + Os::Test::FileSystem::Tester::GetSetWorkingDirectory cwd_rule; + + // Place these rules into a list of rules + STest::Rule* rules[] = { + &directory_exists_rule, + &file_exists_rule, + ¬_exists_rule, + &remove_rule, + &remove_directory_rule, + &touch_rule, + &create_directory_rule, + &move_rule, + &rename_rule, + ©file_rule, + &append_rule, + &append_new_rule, + &file_size_rule, + &free_space_rule, + &cwd_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, + 1000 + ); + // Run! + const U32 numSteps = bounded.run(*tester); + printf("Ran %u steps.\n", numSteps); + +} diff --git a/Os/test/ut/filesystem/CommonTests.hpp b/Os/test/ut/filesystem/CommonTests.hpp new file mode 100644 index 0000000000..276b6c3d1b --- /dev/null +++ b/Os/test/ut/filesystem/CommonTests.hpp @@ -0,0 +1,28 @@ +// ====================================================================== +// \title Os/test/ut/filesystem/CommonTests.hpp +// \brief GoogleTest fixture definitions used in common FileSystem testing +// ====================================================================== +#ifndef OS_TEST_UT_COMMON_FILESYSTEM_TESTS_HPP +#define OS_TEST_UT_COMMON_FILESYSTEM_TESTS_HPP + +#include +#include +#include + + +class Functionality : public ::testing::Test { + public: + //! Constructor + Functionality(); + + //! SetUp test fixture + void SetUp() override; + + //! TearDown test fixture for safe destruction + void TearDown() override; + + //! Tester/state implementation + std::unique_ptr tester; +}; + +#endif // OS_TEST_UT_COMMON_FILESYSTEM_TESTS_HPP diff --git a/Os/test/ut/filesystem/FileSystemRules.cpp b/Os/test/ut/filesystem/FileSystemRules.cpp new file mode 100644 index 0000000000..83f3fd52b9 --- /dev/null +++ b/Os/test/ut/filesystem/FileSystemRules.cpp @@ -0,0 +1,390 @@ +// ====================================================================== +// \title Os/test/ut/filesystem/FileSystemRules.cpp +// \brief rule implementations for common testing of filesystem +// ====================================================================== + +#include "RulesHeaders.hpp" +#include "FileSystemRules.hpp" +#include "STest/Pick/Pick.hpp" + +// ------------------------------------------------------------------------------------------------------ +// Utility functions +// ------------------------------------------------------------------------------------------------------ + +bool compare_file_contents_on_disk(std::string path1, std::string path2) { + Os::File file1, file2; + file1.open(path1.c_str(), Os::File::OPEN_READ); + file2.open(path2.c_str(), Os::File::OPEN_READ); + + const FwSignedSizeType chunk_size = 128; + + FwSignedSizeType file1Size, file2Size; + file1.size(file1Size); + file2.size(file2Size); + if (file1Size != file2Size) { + return false; + } + const FwIndexType loopLimit = file1Size / chunk_size + 2; + + U8 buffer1[chunk_size], buffer2[chunk_size]; + FwSignedSizeType bytesRead1 = chunk_size, bytesRead2 = chunk_size; + + for (FwIndexType i = 0; i < loopLimit; i++) { + file1.read(buffer1, bytesRead1); + file2.read(buffer2, bytesRead2); + if (bytesRead1 != bytesRead2 || memcmp(buffer1, buffer2, bytesRead1) != 0) { + return false; + } + if (bytesRead1 < chunk_size) { + return true; // End of file reached + } + } + return false; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: DirectoryExists +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::DirectoryExists::DirectoryExists() : + STest::Rule("DirectoryExists") {} + +bool Os::Test::FileSystem::Tester::DirectoryExists::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_dirs.size() > 0; // should always be true in random testing +} + +void Os::Test::FileSystem::Tester::DirectoryExists::action(Os::Test::FileSystem::Tester &state) { + std::string dirPath = state.get_random_directory().path; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dirPath.c_str())) << "exists() failed for directory"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: FileExists +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::FileExists::FileExists() : + STest::Rule("FileExists") {} + +bool Os::Test::FileSystem::Tester::FileExists::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::FileExists::action(Os::Test::FileSystem::Tester &state) { + std::string filename = state.get_random_file().path; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(filename.c_str())) << "exists() failed for file"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: PathNotExists +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::PathNotExists::PathNotExists() : + STest::Rule("PathNotExists") {} + +bool Os::Test::FileSystem::Tester::PathNotExists::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::PathNotExists::action(Os::Test::FileSystem::Tester &state) { + ASSERT_FALSE(Os::FileSystem::getSingleton().exists("does_not_exist")) + << "exists() failed to return false for non-existent path"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: RemoveFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::RemoveFile::RemoveFile() : + STest::Rule("RemoveFile") {} + +bool Os::Test::FileSystem::Tester::RemoveFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::RemoveFile::action(Os::Test::FileSystem::Tester &state) { + std::string filepath = state.get_random_file().path; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(filepath.c_str())); + Os::FileSystem::Status status = Os::FileSystem::getSingleton().removeFile(filepath.c_str()); + state.remove_file(filepath); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to remove test file"; + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(filepath.c_str())) << "exists() failed for touched file"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: TouchFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::TouchFile::TouchFile() : + STest::Rule("TouchFile") {} + +bool Os::Test::FileSystem::Tester::TouchFile::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::TouchFile::action(Os::Test::FileSystem::Tester &state) { + std::string new_filename = state.new_random_filepath(); + Os::FileSystem::Status status; + status = Os::FileSystem::getSingleton().touch(new_filename.c_str()); + state.touch_file(new_filename); + // Check that the file was created + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to touch file"; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(new_filename.c_str())) << "exists() failed for touched file"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: CreateDirectory +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::CreateDirectory::CreateDirectory() : + STest::Rule("CreateDirectory") {} + +bool Os::Test::FileSystem::Tester::CreateDirectory::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::CreateDirectory::action(Os::Test::FileSystem::Tester &state) { + std::string dirpath = state.get_random_directory().path + "/" + state.get_new_dirname(); + Os::FileSystem::Status status; + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(dirpath.c_str())); + status = Os::FileSystem::getSingleton().createDirectory(dirpath.c_str()); + state.create_directory(dirpath); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to create test directory"; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dirpath.c_str())) << "exists() should return true for created directory"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: RemoveDirectory +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::RemoveDirectory::RemoveDirectory() : + STest::Rule("RemoveDirectory") {} + +bool Os::Test::FileSystem::Tester::RemoveDirectory::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::RemoveDirectory::action(Os::Test::FileSystem::Tester &state) { + // We create a new directory to be removed because we need to ensure that the directory is empty + // and this is the simplest way to do so. CreateDirectory is already tested above. + std::string dirpath = state.get_random_directory().path + "/to_be_removed"; + Os::FileSystem::Status status; + status = Os::FileSystem::getSingleton().createDirectory(dirpath.c_str()); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to create test directory"; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dirpath.c_str())) << "exists() failed for created test directory"; + status = Os::FileSystem::getSingleton().removeDirectory(dirpath.c_str()); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to remove test directory"; + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(dirpath.c_str())) << "exists() should return false for removed directory"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: RenameFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::RenameFile::RenameFile() : + STest::Rule("RenameFile") {} + +bool Os::Test::FileSystem::Tester::RenameFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::RenameFile::action(Os::Test::FileSystem::Tester &state) { + TestFile& file = state.get_random_file(); + std::string source_path = file.path; + std::string original_content = file.contents; + + std::string dest_path = state.new_random_filepath(); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + Os::FileSystem::Status status; + status = Os::FileSystem::getSingleton().rename(source_path.c_str(), dest_path.c_str()); + state.move_file(file, dest_path); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to rename file"; + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + + // Assert file contents on disk + ASSERT_TRUE(state.validate_contents_on_disk(file)); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: MoveFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::MoveFile::MoveFile() : + STest::Rule("MoveFile") {} + +bool Os::Test::FileSystem::Tester::MoveFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::MoveFile::action(Os::Test::FileSystem::Tester &state) { + TestFile& file = state.get_random_file(); + std::string source_path = file.path; + std::string original_content = file.contents; + + std::string dest_path = state.new_random_filepath(); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + Os::FileSystem::Status status; + status = Os::FileSystem::getSingleton().moveFile(source_path.c_str(), dest_path.c_str()); + state.move_file(file, dest_path); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to move file"; + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + + // Assert file contents on disk + ASSERT_TRUE(state.validate_contents_on_disk(file)); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::CopyFile::CopyFile() : + STest::Rule("CopyFile") {} + +bool Os::Test::FileSystem::Tester::CopyFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::CopyFile::action(Os::Test::FileSystem::Tester &state) { + Os::FileSystem::Status status; + + TestFile& source = state.get_random_file(); + std::string source_path = source.path; + std::string dest_path = state.new_random_filepath(); + + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_FALSE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + status = Os::FileSystem::getSingleton().copyFile(source_path.c_str(), dest_path.c_str()); + state.copy_file(source, dest_path); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to move file"; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + + // Compare contents of source and dest on disk + ASSERT_TRUE(compare_file_contents_on_disk(source_path, dest_path)); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: AppendFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::AppendFile::AppendFile() : + STest::Rule("AppendFile") {} + +bool Os::Test::FileSystem::Tester::AppendFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::AppendFile::action(Os::Test::FileSystem::Tester &state) { + Os::FileSystem::Status status; + TestFile& source = state.get_random_file(); + std::string source_path = source.path; + TestFile& dest = state.get_random_file(); + std::string dest_path = dest.path; + + bool createMissingDest = false; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(dest_path.c_str())); + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + status = Os::FileSystem::getSingleton().appendFile(source_path.c_str(), dest_path.c_str(), createMissingDest); + state.append_file(source, dest, createMissingDest); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to append file"; + // Compare contents of dest on disk with expected contents + ASSERT_TRUE(state.validate_contents_on_disk(dest)); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: AppendToNewFile +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::AppendToNewFile::AppendToNewFile() : + STest::Rule("AppendToNewFile") {} + +bool Os::Test::FileSystem::Tester::AppendToNewFile::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::AppendToNewFile::action(Os::Test::FileSystem::Tester &state) { + Os::FileSystem::Status status; + TestFile& source = state.get_random_file(); + std::string source_path = source.path; + TestFile& dest = state.get_random_file(); + std::string dest_path = dest.path; + + if (source_path == dest_path) { + return; // skip test - can not remove dest if it is the same as source + } + // Set up test state: remove dest file from disk and reset contents in test state + bool createMissingDest = true; + Os::FileSystem::getSingleton().removeFile(dest_path.c_str()); + dest.contents = ""; + ASSERT_TRUE(Os::FileSystem::getSingleton().exists(source_path.c_str())); + // Perform append operation + status = Os::FileSystem::getSingleton().appendFile(source_path.c_str(), dest_path.c_str(), createMissingDest); + state.append_file(source, dest, createMissingDest); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to append file"; + // Compare contents of dest on disk with expected contents + ASSERT_TRUE(state.validate_contents_on_disk(dest)); +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFileSize +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::GetFileSize::GetFileSize() : + STest::Rule("GetFileSize") {} + +bool Os::Test::FileSystem::Tester::GetFileSize::precondition(const Os::Test::FileSystem::Tester &state) { + return state.m_test_files.size() > 0; +} + +void Os::Test::FileSystem::Tester::GetFileSize::action(Os::Test::FileSystem::Tester &state) { + TestFile file = state.get_random_file(); + Os::FileSystem::Status status; + FwSignedSizeType size; + status = Os::FileSystem::getSingleton().getFileSize(file.path.c_str(), size); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to get file size"; + ASSERT_EQ(size, file.contents.size()) << "File size should match contents size"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFreeSpace +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::GetFreeSpace::GetFreeSpace() : + STest::Rule("GetFreeSpace") {} + +bool Os::Test::FileSystem::Tester::GetFreeSpace::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::GetFreeSpace::action(Os::Test::FileSystem::Tester &state) { + Os::FileSystem::Status status; + FwSizeType totalBytes = 0, freeBytes = 0; + status = Os::FileSystem::getSingleton().getFreeSpace("/", totalBytes, freeBytes); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to run getFreeSpace"; + ASSERT_GT(totalBytes, 0) << "Total bytes should be greater than 0"; + ASSERT_GT(freeBytes, 0) << "Free bytes should be greater than 0"; + ASSERT_GT(totalBytes, freeBytes) << "Total bytes should be greater than free bytes"; +} + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetSetWorkingDirectory: Test both get and set working directory +// ------------------------------------------------------------------------------------------------------ +Os::Test::FileSystem::Tester::GetSetWorkingDirectory::GetSetWorkingDirectory() : + STest::Rule("GetSetWorkingDirectory") {} + +bool Os::Test::FileSystem::Tester::GetSetWorkingDirectory::precondition(const Os::Test::FileSystem::Tester &state) { + return true; +} + +void Os::Test::FileSystem::Tester::GetSetWorkingDirectory::action(Os::Test::FileSystem::Tester &state) { + Os::FileSystem::Status status; + FwSizeType cwdSize = PATH_MAX; + char cwdBuffer[cwdSize]; + + // Get original working directory + status = Os::FileSystem::getSingleton().getWorkingDirectory(cwdBuffer, cwdSize); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to get original working directory"; + std::string original_cwd(cwdBuffer); + + // Change working directory + std::string other_dir = state.get_random_directory().path; + status = Os::FileSystem::getSingleton().changeWorkingDirectory(other_dir.c_str()); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to change working directory"; + status = Os::FileSystem::getSingleton().getWorkingDirectory(cwdBuffer, cwdSize); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to change working directory back"; + ASSERT_TRUE(other_dir.compare(cwdBuffer)) << "getWorkingDirectory did not return the expected directory"; + + // Change back to original working directory + // This is done so that this test does not affect the working directory of other tests during random testing + status = Os::FileSystem::getSingleton().changeWorkingDirectory(original_cwd.c_str()); + ASSERT_EQ(status, Os::FileSystem::Status::OP_OK) << "Failed to change working directory back"; +} diff --git a/Os/test/ut/filesystem/FileSystemRules.hpp b/Os/test/ut/filesystem/FileSystemRules.hpp new file mode 100644 index 0000000000..8449980899 --- /dev/null +++ b/Os/test/ut/filesystem/FileSystemRules.hpp @@ -0,0 +1,156 @@ +// ====================================================================== +// \title Os/test/ut/filesystem/FileSystemRules.hpp +// \brief rule definitions for common testing of filesystem +// ====================================================================== +// Stripped when compiled, here for IDEs +#include "RulesHeaders.hpp" + +// ------------------------------------------------------------------------------------------------------ +// Rule: DirectoryExists: Check that exists() returns true for an existing directory +// ------------------------------------------------------------------------------------------------------ +struct DirectoryExists : public STest::Rule { + // ---------------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------------- + + //! Constructor + DirectoryExists(); + + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Precondition + bool precondition(const Os::Test::FileSystem::Tester &state //!< The test state + ); + + //! Action + void action(Os::Test::FileSystem::Tester &state //!< The test state + ); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: FileExists: Check that exists() returns false for an existing file +// ------------------------------------------------------------------------------------------------------ +struct FileExists : public STest::Rule { + FileExists(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: PathNotExists: Check that exists() returns false for a non-existing path +// ------------------------------------------------------------------------------------------------------ +struct PathNotExists : public STest::Rule { + PathNotExists(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: RemoveFile: Check that removeFile() removes a file +// ------------------------------------------------------------------------------------------------------ +struct RemoveFile : public STest::Rule { + RemoveFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: TouchFile: Check that touch() creates a file +// ------------------------------------------------------------------------------------------------------ +struct TouchFile : public STest::Rule { + TouchFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: RemoveDirectory: Check directory removal +// ------------------------------------------------------------------------------------------------------ +struct RemoveDirectory : public STest::Rule { + RemoveDirectory(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: CreateDirectory: Check directory creation +// ------------------------------------------------------------------------------------------------------ +struct CreateDirectory : public STest::Rule { + CreateDirectory(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: RenameFile: Move a file from one location to another +// ------------------------------------------------------------------------------------------------------ +struct RenameFile : public STest::Rule { + RenameFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: MoveFile: Move a file from one location to another +// ------------------------------------------------------------------------------------------------------ +struct MoveFile : public STest::Rule { + MoveFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: CopyFile: Copy a file from one location to another +// ------------------------------------------------------------------------------------------------------ +struct CopyFile : public STest::Rule { + CopyFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: AppendFile: Append a file to another existing file +// ------------------------------------------------------------------------------------------------------ +struct AppendFile : public STest::Rule { + AppendFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: AppendToNewFile: Append file to a new path, using createMissingDest +// ------------------------------------------------------------------------------------------------------ +struct AppendToNewFile : public STest::Rule { + AppendToNewFile(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFileSize: Test the GetFileSize function +// ------------------------------------------------------------------------------------------------------ +struct GetFileSize : public STest::Rule { + GetFileSize(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetFreeSpace: Test the GetFreeSpace function +// ------------------------------------------------------------------------------------------------------ +struct GetFreeSpace : public STest::Rule { + GetFreeSpace(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; + +// ------------------------------------------------------------------------------------------------------ +// Rule: GetSetWorkingDirectory: Get working directory, set working directory +// ------------------------------------------------------------------------------------------------------ +struct GetSetWorkingDirectory : public STest::Rule { + GetSetWorkingDirectory(); + bool precondition(const Os::Test::FileSystem::Tester &state); + void action(Os::Test::FileSystem::Tester &state); +}; diff --git a/Os/test/ut/filesystem/RulesHeaders.hpp b/Os/test/ut/filesystem/RulesHeaders.hpp new file mode 100644 index 0000000000..75d8167c27 --- /dev/null +++ b/Os/test/ut/filesystem/RulesHeaders.hpp @@ -0,0 +1,156 @@ +// ====================================================================== +// \title Os/test/ut/filesystem/RulesHeaders.hpp +// \brief rule definitions for common testing +// ====================================================================== +#ifndef __RULES_HEADERS__ +#define __RULES_HEADERS__ + +#include +#include "Os/FileSystem.hpp" +#include "STest/Rule/Rule.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" +#include "STest/Scenario/Scenario.hpp" +#include "STest/Pick/Pick.hpp" + +namespace Os { +namespace Test { +namespace FileSystem { + +struct FileSystemNode { + std::string path; + explicit FileSystemNode(std::string path) : path(path) {}; + bool operator==(const FileSystemNode& other) const { + return this->path == other.path; + } +}; +struct TestFile : FileSystemNode { + std::string contents; + TestFile(std::string path, std::string contents) : FileSystemNode(path), contents(contents) {}; +}; //!< Representation of a file for tracking state of the filesystem during testing + +struct TestDirectory : FileSystemNode { + explicit TestDirectory(std::string path) : FileSystemNode(path) {}; +}; //!< Representation of a directory for tracking state of the filesystem during testing + + +struct Tester { + + // Constructors that ensures the filesystem is always valid + Tester() = default; + + // Destructor must be virtual + virtual ~Tester() = default; + + //! State representation of a section of FileSystem (i.e. a test directory) + //! This only tracks relative paths, and not hierarchy. This means that all files + //! and directories that are nested within the root of the test directory are all + //! tracked in the same vector. This is for simplicity, because hierarchy is not + //! needed for the tests. + std::vector m_test_dirs; + std::vector m_test_files; + + FwIndexType m_counter; //!< Counter for generating unique file/directory names + + // --------------------------------------------------------------- + // Functions to manipulate the state of the Tester w.r.t filesystem + // --------------------------------------------------------------- + void touch_file(std::string path) { + this->m_test_files.push_back(TestFile(path, "")); + } + void create_directory(std::string path) { + this->m_test_dirs.push_back(TestDirectory(path)); + } + void remove_file(std::string path) { + for (auto it = this->m_test_files.begin(); it != this->m_test_files.end(); ++it) { + if (it->path == path) { + this->m_test_files.erase(it); + return; + } + } + } + void move_file(TestFile& source, std::string dest_path) { + source.path = dest_path; + } + void copy_file(TestFile& source, std::string dest_path) { + TestFile new_file(dest_path, source.contents); + this->m_test_files.push_back(new_file); + } + void append_file(TestFile& source_file, TestFile& dest_file, bool createMissingDest) { + dest_file.contents += source_file.contents; + } + + // ---------------------------------------------------------------- + // Helper functions for testing + // ---------------------------------------------------------------- + std::string get_new_filename() { + return "test_file_" + std::to_string(m_counter++); + } + std::string get_new_dirname() { + return "test_dir_" + std::to_string(m_counter++); + } + TestFile& get_random_file() { + return this->m_test_files[STest::Pick::lowerUpper(0, this->m_test_files.size() - 1)]; + } + TestDirectory& get_random_directory() { + return this->m_test_dirs[STest::Pick::lowerUpper(0, this->m_test_dirs.size() - 1)]; + } + std::string new_random_filepath() { + return get_random_directory().path + "/" + get_new_filename(); + } + + bool validate_contents_on_disk(TestFile& file) { + Os::File os_file; + os_file.open(file.path.c_str(), Os::File::OPEN_READ); + FwSignedSizeType size; + os_file.size(size); + if (size == 0) { + os_file.close(); + return file.contents.empty(); + } + U8 buffer[size]; + os_file.read(buffer, size); + os_file.close(); + std::string disk_contents(reinterpret_cast(buffer), size); + return disk_contents == file.contents; + } + + // ---------------------------------------------------------------- + // Helpers to write and remove test state from disk + // ---------------------------------------------------------------- + void write_test_state_to_disk() { + Os::File file; + Os::Directory dir; + // Create and write directories + for (TestDirectory& dir_track : this->m_test_dirs) { + dir.open(dir_track.path.c_str(), Os::Directory::OpenMode::CREATE_IF_MISSING); + dir.close(); + this->m_counter++; + } + // Create and write files + for (TestFile& file_track : this->m_test_files) { + file.open(file_track.path.c_str(), Os::File::OPEN_CREATE); + FwSignedSizeType bytesRead = file_track.contents.size(); + file.write(reinterpret_cast(file_track.contents.c_str()), bytesRead); + file.close(); + this->m_counter++; + } + } + + void purge_test_state_from_disk() { + for (auto filename : this->m_test_files) { + Os::FileSystem::removeFile(filename.path.c_str()); + } + for (auto it = this->m_test_dirs.rbegin(); it != this->m_test_dirs.rend(); ++it) { + Os::FileSystem::removeDirectory(it->path.c_str()); + } + } + +// Do NOT alter, adds rules to Tester as inner classes +#include "FileSystemRules.hpp" +}; + +} // namespace FileSystem +} // namespace Test +} // namespace Os +#endif // __RULES_HEADERS__ diff --git a/RPI/Main.cpp b/RPI/Main.cpp index f4b6955e81..131ae7672c 100644 --- a/RPI/Main.cpp +++ b/RPI/Main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,7 @@ static void sighandler(int signum) { } int main(int argc, char* argv[]) { - Os::Console::init(); + Os::init(); I32 option = 0; while ((option = getopt(argc, argv, "hp:a:")) != -1){ diff --git a/RPI/RpiDemo/RpiDemoComponentImpl.cpp b/RPI/RpiDemo/RpiDemoComponentImpl.cpp index d133f3bd65..b200240c3a 100644 --- a/RPI/RpiDemo/RpiDemoComponentImpl.cpp +++ b/RPI/RpiDemo/RpiDemoComponentImpl.cpp @@ -38,15 +38,6 @@ namespace RPI { } - void RpiDemoComponentImpl :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - RpiDemoComponentBase::init(queueDepth, instance); - } - RpiDemoComponentImpl :: ~RpiDemoComponentImpl() { diff --git a/RPI/RpiDemo/RpiDemoComponentImpl.hpp b/RPI/RpiDemo/RpiDemoComponentImpl.hpp index 27302d7901..a1b13a7580 100644 --- a/RPI/RpiDemo/RpiDemoComponentImpl.hpp +++ b/RPI/RpiDemo/RpiDemoComponentImpl.hpp @@ -46,13 +46,6 @@ namespace RPI { const char *const compName /*!< The component name*/ ); - //! Initialize object RpiDemo - //! - void init( - const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object RpiDemo //! ~RpiDemoComponentImpl(); diff --git a/RPI/Top/RPITopologyDefs.hpp b/RPI/Top/RPITopologyDefs.hpp index f434f2e9b0..50ec1d1c84 100644 --- a/RPI/Top/RPITopologyDefs.hpp +++ b/RPI/Top/RPITopologyDefs.hpp @@ -46,15 +46,15 @@ namespace RPI { // Health ping entries namespace PingEntries { - namespace rateGroup10HzComp { enum { WARN = 3, FATAL = 5 }; } - namespace rateGroup1HzComp { enum { WARN = 3, FATAL = 5 }; } - namespace cmdDisp { enum { WARN = 3, FATAL = 5 }; } - namespace cmdSeq { enum { WARN = 3, FATAL = 5 }; } - namespace chanTlm { enum { WARN = 3, FATAL = 5 }; } - namespace eventLogger { enum { WARN = 3, FATAL = 5 }; } - namespace prmDb { enum { WARN = 3, FATAL = 5 }; } - namespace fileDownlink { enum { WARN = 3, FATAL = 5 }; } - namespace fileUplink { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_rateGroup10HzComp { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_rateGroup1HzComp { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_cmdDisp { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_cmdSeq { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_chanTlm { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_eventLogger { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_prmDb { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_fileDownlink { enum { WARN = 3, FATAL = 5 }; } + namespace RPI_fileUplink { enum { WARN = 3, FATAL = 5 }; } } } diff --git a/RPI/Top/instances.fpp b/RPI/Top/instances.fpp index 992aced0cc..79b7531fe1 100644 --- a/RPI/Top/instances.fpp +++ b/RPI/Top/instances.fpp @@ -27,9 +27,9 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - rateGroup10HzComp.configure( - ConfigObjects::rateGroup10HzComp::context, - FW_NUM_ARRAY_ELEMENTS(ConfigObjects::rateGroup10HzComp::context) + RPI::rateGroup10HzComp.configure( + ConfigObjects::RPI_rateGroup10HzComp::context, + FW_NUM_ARRAY_ELEMENTS(ConfigObjects::RPI_rateGroup10HzComp::context) ); """ @@ -51,8 +51,8 @@ module RPI { priority 20 \ { phase Fpp.ToCpp.Phases.readParameters """ - prmDb.configure("PrmDb.dat"); - prmDb.readParamFile(); + RPI::prmDb.configure("PrmDb.dat"); + RPI::prmDb.readParamFile(); """ } @@ -72,18 +72,18 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - using namespace ConfigConstants::cmdSeq; - cmdSeq.allocateBuffer( + using namespace ConfigConstants::RPI_cmdSeq; + RPI::cmdSeq.allocateBuffer( 0, Allocation::mallocator, - ConfigConstants::cmdSeq::BUFFER_SIZE + ConfigConstants::RPI_cmdSeq::BUFFER_SIZE ); - cmdSeq.setTimeout(TIMEOUT); + RPI::cmdSeq.setTimeout(TIMEOUT); } """ phase Fpp.ToCpp.Phases.tearDownComponents """ - cmdSeq.deallocateBuffer(Allocation::mallocator); + RPI::cmdSeq.deallocateBuffer(Allocation::mallocator); """ } @@ -104,9 +104,9 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - rateGroup1HzComp.configure( - ConfigObjects::rateGroup1HzComp::context, - FW_NUM_ARRAY_ELEMENTS(ConfigObjects::rateGroup1HzComp::context) + RPI::rateGroup1HzComp.configure( + ConfigObjects::RPI_rateGroup1HzComp::context, + FW_NUM_ARRAY_ELEMENTS(ConfigObjects::RPI_rateGroup1HzComp::context) ); """ @@ -133,11 +133,11 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - fileDownlink.configure( - ConfigConstants::fileDownlink::TIMEOUT, - ConfigConstants::fileDownlink::COOLDOWN, - ConfigConstants::fileDownlink::CYCLE_TIME, - ConfigConstants::fileDownlink::FILE_QUEUE_DEPTH + RPI::fileDownlink.configure( + ConfigConstants::RPI_fileDownlink::TIMEOUT, + ConfigConstants::RPI_fileDownlink::COOLDOWN, + ConfigConstants::RPI_fileDownlink::CYCLE_TIME, + ConfigConstants::RPI_fileDownlink::FILE_QUEUE_DEPTH ); """ @@ -163,10 +163,10 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - health.setPingEntries( - ConfigObjects::health::pingEntries, - FW_NUM_ARRAY_ELEMENTS(ConfigObjects::health::pingEntries), - ConfigConstants::health::WATCHDOG_CODE + RPI::health.setPingEntries( + ConfigObjects::RPI_health::pingEntries, + FW_NUM_ARRAY_ELEMENTS(ConfigObjects::RPI_health::pingEntries), + ConfigConstants::RPI_health::WATCHDOG_CODE ); """ @@ -193,10 +193,10 @@ module RPI { { Svc::BufferManager::BufferBins bufferBins; memset(&bufferBins, 0, sizeof(bufferBins)); - using namespace ConfigConstants::fileUplinkBufferManager; + using namespace ConfigConstants::RPI_fileUplinkBufferManager; bufferBins.bins[0].bufferSize = STORE_SIZE; bufferBins.bins[0].numBuffers = QUEUE_SIZE; - fileUplinkBufferManager.setup( + RPI::fileUplinkBufferManager.setup( MGR_ID, 0, Allocation::mallocator, @@ -207,7 +207,7 @@ module RPI { """ phase Fpp.ToCpp.Phases.tearDownComponents """ - fileUplinkBufferManager.cleanup(); + RPI::fileUplinkBufferManager.cleanup(); """ } @@ -224,7 +224,7 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - downlink.setup(ConfigObjects::downlink::framing); + RPI::downlink.setup(ConfigObjects::RPI_downlink::framing); """ } @@ -237,7 +237,7 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - uplink.setup(ConfigObjects::uplink::deframing); + RPI::uplink.setup(ConfigObjects::RPI_uplink::deframing); """ } @@ -257,21 +257,21 @@ module RPI { if (state.hostName != nullptr && state.portNumber != 0) { Os::TaskString name("ReceiveTask"); // Uplink is configured for receive so a socket task is started - comm.configure(state.hostName, state.portNumber); - comm.start( + RPI::comm.configure(state.hostName, state.portNumber); + RPI::comm.start( name, - ConfigConstants::comm::PRIORITY, - ConfigConstants::comm::STACK_SIZE + ConfigConstants::RPI_comm::PRIORITY, + ConfigConstants::RPI_comm::STACK_SIZE ); } """ phase Fpp.ToCpp.Phases.stopTasks """ - comm.stop(); + RPI::comm.stop(); """ phase Fpp.ToCpp.Phases.freeThreads """ - (void) comm.join(); + (void) RPI::comm.join(); """ } @@ -282,7 +282,7 @@ module RPI { { phase Fpp.ToCpp.Phases.stopTasks """ - linuxTimer.quit(); + RPI::linuxTimer.quit(); """ } @@ -295,8 +295,8 @@ module RPI { """ phase Fpp.ToCpp.Phases.configComponents """ - rateGroupDriverComp.configure( - ConfigObjects::rateGroupDriverComp::rgDivs + RPI::rateGroupDriverComp.configure( + ConfigObjects::RPI_rateGroupDriverComp::rgDivs ); """ } @@ -323,7 +323,7 @@ module RPI { phase Fpp.ToCpp.Phases.startTasks """ if (Init::status) { - uartDrv.start(); + RPI::uartDrv.start(); } else { Fw::Logger::log("[ERROR] Initialization failed; not starting UART driver\\n"); @@ -331,7 +331,7 @@ module RPI { """ phase Fpp.ToCpp.Phases.stopTasks """ - uartDrv.quitReadThread(); + RPI::uartDrv.quitReadThread(); """ } @@ -341,7 +341,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = ledDrv.open(21, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); + const bool status = RPI::ledDrv.open(21, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); if (!status) { Fw::Logger::log("[ERROR] Could not open LED driver\\n"); Init::status = false; @@ -356,7 +356,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = gpio23Drv.open(23, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); + const bool status = RPI::gpio23Drv.open(23, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); if (!status) { Fw::Logger::log("[ERROR] Could not open GPIO 23 driver\\n"); Init::status = false; @@ -371,7 +371,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = gpio24Drv.open(24, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); + const bool status = RPI::gpio24Drv.open(24, Drv::LinuxGpioDriverComponentImpl::GPIO_OUT); if (!status) { Fw::Logger::log("[ERROR] Could not open GPIO 24 driver\\n"); Init::status = false; @@ -386,7 +386,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = gpio25Drv.open(25, Drv::LinuxGpioDriverComponentImpl::GPIO_IN); + const bool status = RPI::gpio25Drv.open(25, Drv::LinuxGpioDriverComponentImpl::GPIO_IN); if (!status) { Fw::Logger::log("[ERROR] Could not open GPIO 25 driver\\n"); Init::status = false; @@ -401,7 +401,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = gpio17Drv.open(17, Drv::LinuxGpioDriverComponentImpl::GPIO_IN); + const bool status = RPI::gpio17Drv.open(17, Drv::LinuxGpioDriverComponentImpl::GPIO_IN); if (!status) { Fw::Logger::log("[ERROR] Could not open GPIO 17 driver\\n"); Init::status = false; @@ -416,7 +416,7 @@ module RPI { phase Fpp.ToCpp.Phases.configComponents """ { - const bool status = spiDrv.open(0, 0, Drv::SPI_FREQUENCY_1MHZ); + const bool status = RPI::spiDrv.open(0, 0, Drv::SPI_FREQUENCY_1MHZ); if (!status) { Fw::Logger::log("[ERROR] Could not open SPI driver\\n"); Init::status = false; @@ -441,10 +441,10 @@ module RPI { { Svc::BufferManager::BufferBins bufferBins; memset(&bufferBins, 0, sizeof(bufferBins)); - using namespace ConfigConstants::uartBufferManager; + using namespace ConfigConstants::RPI_uartBufferManager; bufferBins.bins[0].bufferSize = STORE_SIZE; bufferBins.bins[0].numBuffers = QUEUE_SIZE; - uartBufferManager.setup( + RPI::uartBufferManager.setup( MGR_ID, 0, Allocation::mallocator, @@ -455,7 +455,7 @@ module RPI { """ phase Fpp.ToCpp.Phases.tearDownComponents """ - uartBufferManager.cleanup(); + RPI::uartBufferManager.cleanup(); """ } diff --git a/RPI/test/int/rpi_integration_test.py b/RPI/test/int/rpi_integration_test.py index dec78dfe13..a77df6408a 100644 --- a/RPI/test/int/rpi_integration_test.py +++ b/RPI/test/int/rpi_integration_test.py @@ -33,7 +33,7 @@ def set_event_filter(fprime_test_api, severity, enabled): severity = FilterSeverity[severity].name try: fprime_test_api.send_command( - "eventLogger.SET_EVENT_FILTER", + "RPI.eventLogger.SET_EVENT_FILTER", [severity, enabled], ) return True @@ -61,21 +61,25 @@ def test_is_streaming(fprime_test_api): def test_send_command(fprime_test_api): - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP", max_delay=0.1) + fprime_test_api.send_and_assert_command("RPI.cmdDisp.CMD_NO_OP", max_delay=0.1) assert fprime_test_api.get_command_test_history().size() == 1 - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP", max_delay=0.1) + fprime_test_api.send_and_assert_command("RPI.cmdDisp.CMD_NO_OP", max_delay=0.1) assert fprime_test_api.get_command_test_history().size() == 2 def test_send_and_assert_no_op(fprime_test_api): length = 100 failed = 0 - evr_seq = ["OpCodeDispatched", "NoOpReceived", "OpCodeCompleted"] + evr_seq = [ + "RPI.cmdDisp.OpCodeDispatched", + "RPI.cmdDisp.NoOpReceived", + "RPI.cmdDisp.OpCodeCompleted", + ] any_reordered = False dropped = False for i in range(0, length): results = fprime_test_api.send_and_await_event( - "cmdDisp.CMD_NO_OP", events=evr_seq, timeout=25 + "RPI.cmdDisp.CMD_NO_OP", events=evr_seq, timeout=25 ) msg = "Send and assert NO_OP Trial #{}".format(i) if not fprime_test_api.test_assert(len(results) == 3, msg, True): @@ -129,8 +133,8 @@ def test_active_logger_filter(fprime_test_api): # Drain time for dispatch events time.sleep(10) - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP") - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP") + fprime_test_api.send_and_assert_command("RPI.cmdDisp.CMD_NO_OP") + fprime_test_api.send_and_assert_command("RPI.cmdDisp.CMD_NO_OP") time.sleep(0.5) @@ -141,8 +145,8 @@ def test_active_logger_filter(fprime_test_api): # Drain time for dispatch events time.sleep(10) fprime_test_api.clear_histories() - fprime_test_api.send_command("cmdDisp.CMD_NO_OP") - fprime_test_api.send_command("cmdDisp.CMD_NO_OP") + fprime_test_api.send_command("RPI.cmdDisp.CMD_NO_OP") + fprime_test_api.send_command("RPI.cmdDisp.CMD_NO_OP") time.sleep(0.5) @@ -168,5 +172,5 @@ def test_seqgen(fprime_test_api): == 0 ), "Failed to run fprime-seqgen" fprime_test_api.send_and_assert_command( - "cmdSeq.CS_RUN", args=["/tmp/ref_test_int.bin", "BLOCK"], max_delay=5 + "RPI.cmdSeq.CS_RUN", args=["/tmp/ref_test_int.bin", "BLOCK"], max_delay=5 ) diff --git a/RPI/test/int/test_seq.seq b/RPI/test/int/test_seq.seq index 480ef0b32f..bda0e6df23 100644 --- a/RPI/test/int/test_seq.seq +++ b/RPI/test/int/test_seq.seq @@ -1,6 +1,6 @@ ; A test sequence ; -R00:00:00 cmdDisp.CMD_NO_OP +R00:00:00 RPI.cmdDisp.CMD_NO_OP ; Let's try out some commands with arguments -R00:00:01.050 cmdDisp.CMD_NO_OP_STRING "Awesome string!"; \ No newline at end of file +R00:00:01.050 RPI.cmdDisp.CMD_NO_OP_STRING "Awesome string!"; \ No newline at end of file diff --git a/Ref/Main.cpp b/Ref/Main.cpp index 0cd5895787..f46062688b 100644 --- a/Ref/Main.cpp +++ b/Ref/Main.cpp @@ -17,7 +17,7 @@ // Used for printf functions #include // Used to get the Os::Console -#include +#include /** @@ -54,7 +54,7 @@ static void signalHandler(int signum) { * @return: 0 on success, something else on failure */ int main(int argc, char* argv[]) { - Os::Console::init(); + Os::init(); U32 port_number = 0; I32 option = 0; char* hostname = nullptr; diff --git a/Ref/PingReceiver/PingReceiverComponentImpl.cpp b/Ref/PingReceiver/PingReceiverComponentImpl.cpp index a4c77f7338..e013779c59 100644 --- a/Ref/PingReceiver/PingReceiverComponentImpl.cpp +++ b/Ref/PingReceiver/PingReceiverComponentImpl.cpp @@ -28,15 +28,6 @@ namespace Ref { } - void PingReceiverComponentImpl :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - PingReceiverComponentBase::init(queueDepth, instance); - } - PingReceiverComponentImpl :: ~PingReceiverComponentImpl() { diff --git a/Ref/PingReceiver/PingReceiverComponentImpl.hpp b/Ref/PingReceiver/PingReceiverComponentImpl.hpp index 56e287a05d..d2c296154e 100644 --- a/Ref/PingReceiver/PingReceiverComponentImpl.hpp +++ b/Ref/PingReceiver/PingReceiverComponentImpl.hpp @@ -33,13 +33,6 @@ namespace Ref { const char *const compName /*!< The component name*/ ); - //! Initialize object PingReceiver - //! - void init( - const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object PingReceiver //! ~PingReceiverComponentImpl(); diff --git a/Ref/RecvBuffApp/RecvBuffComponentImpl.cpp b/Ref/RecvBuffApp/RecvBuffComponentImpl.cpp index e67da07baa..82492a3a46 100644 --- a/Ref/RecvBuffApp/RecvBuffComponentImpl.cpp +++ b/Ref/RecvBuffApp/RecvBuffComponentImpl.cpp @@ -19,11 +19,6 @@ namespace Ref { this->m_stats.setPacketStatus(PacketRecvStatus::PACKET_STATE_NO_PACKETS); } - - void RecvBuffImpl::init(NATIVE_INT_TYPE instanceId) { - RecvBuffComponentBase::init(instanceId); - } - RecvBuffImpl::~RecvBuffImpl() { } diff --git a/Ref/RecvBuffApp/RecvBuffComponentImpl.hpp b/Ref/RecvBuffApp/RecvBuffComponentImpl.hpp index 56ec538140..42a952cc91 100644 --- a/Ref/RecvBuffApp/RecvBuffComponentImpl.hpp +++ b/Ref/RecvBuffApp/RecvBuffComponentImpl.hpp @@ -11,7 +11,6 @@ namespace Ref { // Only called by derived class RecvBuffImpl(const char* compName); - void init(NATIVE_INT_TYPE instanceId = 0); ~RecvBuffImpl(); private: diff --git a/Ref/SendBuffApp/SendBuffComponentImpl.cpp b/Ref/SendBuffApp/SendBuffComponentImpl.cpp index 5b17c95c2d..1d5a273707 100644 --- a/Ref/SendBuffApp/SendBuffComponentImpl.cpp +++ b/Ref/SendBuffApp/SendBuffComponentImpl.cpp @@ -27,10 +27,6 @@ namespace Ref { } - void SendBuffImpl::init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance) { - SendBuffComponentBase::init(queueDepth,instance); - } - void SendBuffImpl::SchedIn_handler(NATIVE_INT_TYPE portNum, U32 context) { // first, dequeue any messages diff --git a/Ref/SendBuffApp/SendBuffComponentImpl.hpp b/Ref/SendBuffApp/SendBuffComponentImpl.hpp index 7a9e742b74..1fa944d896 100644 --- a/Ref/SendBuffApp/SendBuffComponentImpl.hpp +++ b/Ref/SendBuffApp/SendBuffComponentImpl.hpp @@ -12,7 +12,6 @@ namespace Ref { // Only called by derived class SendBuffImpl(const char* compName); //!< constructor - void init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance = 0); //!< initialization function ~SendBuffImpl(); //!< destructor private: diff --git a/Ref/SignalGen/SignalGen.cpp b/Ref/SignalGen/SignalGen.cpp index 7ec1c9423a..26b6b8f8d2 100644 --- a/Ref/SignalGen/SignalGen.cpp +++ b/Ref/SignalGen/SignalGen.cpp @@ -44,14 +44,6 @@ namespace Ref { m_currDp(0) {} - void SignalGen :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - SignalGenComponentBase::init(queueDepth, instance); - } SignalGen :: ~SignalGen() { } diff --git a/Ref/SignalGen/SignalGen.hpp b/Ref/SignalGen/SignalGen.hpp index 7d716646fc..da592899b1 100644 --- a/Ref/SignalGen/SignalGen.hpp +++ b/Ref/SignalGen/SignalGen.hpp @@ -77,14 +77,6 @@ namespace Ref { const char* compName //!< The component name ); - - - //! Initialize a SignalGen - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); - //! Destroy a SignalGen ~SignalGen(); diff --git a/Ref/Top/RefPackets.xml b/Ref/Top/RefPackets.xml index a8e5204119..898a72bf16 100644 --- a/Ref/Top/RefPackets.xml +++ b/Ref/Top/RefPackets.xml @@ -72,12 +72,7 @@ - - - - - - + diff --git a/Ref/Top/RefTopology.cpp b/Ref/Top/RefTopology.cpp index 9cfd30cffd..07d6434981 100644 --- a/Ref/Top/RefTopology.cpp +++ b/Ref/Top/RefTopology.cpp @@ -64,20 +64,20 @@ enum TopologyConstants { // Ping entries are autocoded, however; this code is not properly exported. Thus, it is copied here. Svc::Health::PingEntry pingEntries[] = { - {PingEntries::blockDrv::WARN, PingEntries::blockDrv::FATAL, "blockDrv"}, - {PingEntries::tlmSend::WARN, PingEntries::tlmSend::FATAL, "chanTlm"}, - {PingEntries::cmdDisp::WARN, PingEntries::cmdDisp::FATAL, "cmdDisp"}, - {PingEntries::cmdSeq::WARN, PingEntries::cmdSeq::FATAL, "cmdSeq"}, - {PingEntries::eventLogger::WARN, PingEntries::eventLogger::FATAL, "eventLogger"}, - {PingEntries::fileDownlink::WARN, PingEntries::fileDownlink::FATAL, "fileDownlink"}, - {PingEntries::fileManager::WARN, PingEntries::fileManager::FATAL, "fileManager"}, - {PingEntries::fileUplink::WARN, PingEntries::fileUplink::FATAL, "fileUplink"}, - {PingEntries::pingRcvr::WARN, PingEntries::pingRcvr::FATAL, "pingRcvr"}, - {PingEntries::prmDb::WARN, PingEntries::prmDb::FATAL, "prmDb"}, - {PingEntries::rateGroup1Comp::WARN, PingEntries::rateGroup1Comp::FATAL, "rateGroup1Comp"}, - {PingEntries::rateGroup2Comp::WARN, PingEntries::rateGroup2Comp::FATAL, "rateGroup2Comp"}, - {PingEntries::rateGroup3Comp::WARN, PingEntries::rateGroup3Comp::FATAL, "rateGroup3Comp"}, - {PingEntries::dpCat::WARN, PingEntries::dpCat::FATAL, "rateGroup3Comp"}, + {PingEntries::Ref_blockDrv::WARN, PingEntries::Ref_blockDrv::FATAL, "blockDrv"}, + {PingEntries::Ref_tlmSend::WARN, PingEntries::Ref_tlmSend::FATAL, "chanTlm"}, + {PingEntries::Ref_cmdDisp::WARN, PingEntries::Ref_cmdDisp::FATAL, "cmdDisp"}, + {PingEntries::Ref_cmdSeq::WARN, PingEntries::Ref_cmdSeq::FATAL, "cmdSeq"}, + {PingEntries::Ref_eventLogger::WARN, PingEntries::Ref_eventLogger::FATAL, "eventLogger"}, + {PingEntries::Ref_fileDownlink::WARN, PingEntries::Ref_fileDownlink::FATAL, "fileDownlink"}, + {PingEntries::Ref_fileManager::WARN, PingEntries::Ref_fileManager::FATAL, "fileManager"}, + {PingEntries::Ref_fileUplink::WARN, PingEntries::Ref_fileUplink::FATAL, "fileUplink"}, + {PingEntries::Ref_pingRcvr::WARN, PingEntries::Ref_pingRcvr::FATAL, "pingRcvr"}, + {PingEntries::Ref_prmDb::WARN, PingEntries::Ref_prmDb::FATAL, "prmDb"}, + {PingEntries::Ref_rateGroup1Comp::WARN, PingEntries::Ref_rateGroup1Comp::FATAL, "rateGroup1Comp"}, + {PingEntries::Ref_rateGroup2Comp::WARN, PingEntries::Ref_rateGroup2Comp::FATAL, "rateGroup2Comp"}, + {PingEntries::Ref_rateGroup3Comp::WARN, PingEntries::Ref_rateGroup3Comp::FATAL, "rateGroup3Comp"}, + {PingEntries::Ref_dpCat::WARN, PingEntries::Ref_dpCat::FATAL, "rateGroup3Comp"}, }; /** diff --git a/Ref/Top/RefTopologyDefs.hpp b/Ref/Top/RefTopologyDefs.hpp index 20ad9bd5d8..ede301899c 100644 --- a/Ref/Top/RefTopologyDefs.hpp +++ b/Ref/Top/RefTopologyDefs.hpp @@ -53,46 +53,46 @@ struct TopologyState { * ``` */ namespace PingEntries { -namespace blockDrv { +namespace Ref_blockDrv { enum { WARN = 3, FATAL = 5 }; } -namespace tlmSend { +namespace Ref_tlmSend { enum { WARN = 3, FATAL = 5 }; } -namespace cmdDisp { +namespace Ref_cmdDisp { enum { WARN = 3, FATAL = 5 }; } -namespace cmdSeq { +namespace Ref_cmdSeq { enum { WARN = 3, FATAL = 5 }; } -namespace eventLogger { +namespace Ref_eventLogger { enum { WARN = 3, FATAL = 5 }; } -namespace fileDownlink { +namespace Ref_fileDownlink { enum { WARN = 3, FATAL = 5 }; } -namespace fileManager { +namespace Ref_fileManager { enum { WARN = 3, FATAL = 5 }; } -namespace fileUplink { +namespace Ref_fileUplink { enum { WARN = 3, FATAL = 5 }; } -namespace pingRcvr { +namespace Ref_pingRcvr { enum { WARN = 3, FATAL = 5 }; } -namespace prmDb { +namespace Ref_prmDb { enum { WARN = 3, FATAL = 5 }; } -namespace rateGroup1Comp { +namespace Ref_rateGroup1Comp { enum { WARN = 3, FATAL = 5 }; } -namespace rateGroup2Comp { +namespace Ref_rateGroup2Comp { enum { WARN = 3, FATAL = 5 }; } -namespace rateGroup3Comp { +namespace Ref_rateGroup3Comp { enum { WARN = 3, FATAL = 5 }; } -namespace dpCat { +namespace Ref_dpCat { enum { WARN = 3, FATAL = 5 }; } } // namespace PingEntries diff --git a/Ref/TypeDemo/TypeDemo.cpp b/Ref/TypeDemo/TypeDemo.cpp index 29b7d83a8f..3a290a693b 100644 --- a/Ref/TypeDemo/TypeDemo.cpp +++ b/Ref/TypeDemo/TypeDemo.cpp @@ -16,10 +16,6 @@ namespace Ref { TypeDemo ::TypeDemo(const char* const compName) : TypeDemoComponentBase(compName) {} -void TypeDemo ::init(const NATIVE_INT_TYPE instance) { - TypeDemoComponentBase::init(instance); -} - // ---------------------------------------------------------------------- // Command handler implementations // ---------------------------------------------------------------------- diff --git a/Ref/TypeDemo/TypeDemo.hpp b/Ref/TypeDemo/TypeDemo.hpp index ae2193c3d4..3583ce62a2 100644 --- a/Ref/TypeDemo/TypeDemo.hpp +++ b/Ref/TypeDemo/TypeDemo.hpp @@ -22,11 +22,6 @@ class TypeDemo : public TypeDemoComponentBase { TypeDemo(const char* const compName //!< The component name ); - //! Initialize object TypeDemo - //! - void init(const NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy object TypeDemo //! ~TypeDemo() = default; diff --git a/Ref/test/int/ref_integration_test.py b/Ref/test/int/ref_integration_test.py index 6682fa2882..8523e42e01 100644 --- a/Ref/test/int/ref_integration_test.py +++ b/Ref/test/int/ref_integration_test.py @@ -2,6 +2,7 @@ A set of integration tests to apply to the Ref app. This is intended to be a reference of integration testing. """ + import subprocess import time from enum import Enum @@ -24,14 +25,14 @@ def test_is_streaming(fprime_test_api): """Test flight software is streaming Tests that the flight software is streaming by looking for 5 telemetry items in 10 seconds. Additionally, - "sendBuffComp.SendState" is verified to be SEND_IDLE. + "Ref.sendBuffComp.SendState" is verified to be SEND_IDLE. """ results = fprime_test_api.assert_telemetry_count(5, timeout=10) for result in results: msg = "received channel {} update: {}".format(result.get_id(), result.get_str()) print(msg) fprime_test_api.assert_telemetry( - "sendBuffComp.SendState", value="SEND_IDLE", timeout=3 + "Ref.sendBuffComp.SendState", value="SEND_IDLE", timeout=3 ) @@ -56,7 +57,7 @@ def set_event_filter(fprime_test_api, severity, enabled): severity = FilterSeverity[severity].name try: fprime_test_api.send_command( - "eventLogger.SET_EVENT_FILTER", + "Ref.eventLogger.SET_EVENT_FILTER", [severity, enabled], ) return True @@ -79,9 +80,9 @@ def test_send_command(fprime_test_api): Tests command send, dispatch, and receipt using send_and_assert command with a pair of NO-OP commands. """ - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP", max_delay=0.1) + fprime_test_api.send_and_assert_command("Ref.cmdDisp.CMD_NO_OP", max_delay=0.1) assert fprime_test_api.get_command_test_history().size() == 1 - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP", max_delay=0.1) + fprime_test_api.send_and_assert_command("Ref.cmdDisp.CMD_NO_OP", max_delay=0.1) assert fprime_test_api.get_command_test_history().size() == 2 @@ -91,9 +92,11 @@ def test_send_command_args(fprime_test_api): Tests command send, dispatch, and receipt using send_and_assert command with a pair of NO-OP string commands. """ for count, value in enumerate(["Test String 1", "Some other string"], 1): - events = [fprime_test_api.get_event_pred("cmdDisp.NoOpStringReceived", [value])] + events = [ + fprime_test_api.get_event_pred("Ref.cmdDisp.NoOpStringReceived", [value]) + ] fprime_test_api.send_and_assert_command( - "cmdDisp.CMD_NO_OP_STRING", + "Ref.cmdDisp.CMD_NO_OP_STRING", [ value, ], @@ -112,15 +115,15 @@ def test_send_and_assert_no_op(fprime_test_api): length = 100 failed = 0 evr_seq = [ - "cmdDisp.OpCodeDispatched", - "cmdDisp.NoOpReceived", - "cmdDisp.OpCodeCompleted", + "Ref.cmdDisp.OpCodeDispatched", + "Ref.cmdDisp.NoOpReceived", + "Ref.cmdDisp.OpCodeCompleted", ] any_reordered = False dropped = False for i in range(0, length): results = fprime_test_api.send_and_await_event( - "cmdDisp.CMD_NO_OP", events=evr_seq, timeout=25 + "Ref.cmdDisp.CMD_NO_OP", events=evr_seq, timeout=25 ) msg = "Send and assert NO_OP Trial #{}".format(i) if not fprime_test_api.test_assert(len(results) == 3, msg, True): @@ -167,7 +170,7 @@ def test_bd_cycles_ascending(fprime_test_api): length = 60 count_pred = predicates.greater_than(length - 1) results = fprime_test_api.await_telemetry_count( - count_pred, "blockDrv.BD_Cycles", timeout=length + count_pred, "Ref.blockDrv.BD_Cycles", timeout=length ) last = None reordered = False @@ -227,8 +230,8 @@ def test_active_logger_filter(fprime_test_api): # Drain time for dispatch events time.sleep(10) - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP") - fprime_test_api.send_and_assert_command("cmdDisp.CMD_NO_OP") + fprime_test_api.send_and_assert_command("Ref.cmdDisp.CMD_NO_OP") + fprime_test_api.send_and_assert_command("Ref.cmdDisp.CMD_NO_OP") time.sleep(0.5) @@ -239,8 +242,8 @@ def test_active_logger_filter(fprime_test_api): # Drain time for dispatch events time.sleep(10) fprime_test_api.clear_histories() - fprime_test_api.send_command("cmdDisp.CMD_NO_OP") - fprime_test_api.send_command("cmdDisp.CMD_NO_OP") + fprime_test_api.send_command("Ref.cmdDisp.CMD_NO_OP") + fprime_test_api.send_command("Ref.cmdDisp.CMD_NO_OP") time.sleep(0.5) @@ -253,17 +256,17 @@ def test_active_logger_filter(fprime_test_api): def test_signal_generation(fprime_test_api): """Tests the behavior of signal gen component""" fprime_test_api.send_and_assert_command( - "SG4.SignalGen_Settings", [1, 5, 0, "SQUARE"] + "Ref.SG4.SignalGen_Settings", [1, 5, 0, "SQUARE"] ) # First telemetry item should fill only the first slot of the history history = [0, 0, 0, 5] pair_history = [{"time": 0, "value": value} for value in history] info = {"type": "SQUARE", "history": history, "pairHistory": pair_history} - fprime_test_api.send_and_assert_command("SG4.SignalGen_Toggle") - fprime_test_api.assert_telemetry("SG4.History", history, timeout=6) - fprime_test_api.assert_telemetry("SG4.PairHistory", pair_history, timeout=1) - fprime_test_api.assert_telemetry("SG4.Info", info, timeout=1) - fprime_test_api.send_and_assert_command("SG4.SignalGen_Toggle") + fprime_test_api.send_and_assert_command("Ref.SG4.SignalGen_Toggle") + fprime_test_api.assert_telemetry("Ref.SG4.History", history, timeout=6) + fprime_test_api.assert_telemetry("Ref.SG4.PairHistory", pair_history, timeout=1) + fprime_test_api.assert_telemetry("Ref.SG4.Info", info, timeout=1) + fprime_test_api.send_and_assert_command("Ref.SG4.SignalGen_Toggle") def test_seqgen(fprime_test_api): @@ -282,5 +285,5 @@ def test_seqgen(fprime_test_api): == 0 ), "Failed to run fprime-seqgen" fprime_test_api.send_and_assert_command( - "cmdSeq.CS_RUN", args=["/tmp/ref_test_int.bin", "BLOCK"], max_delay=5 + "Ref.cmdSeq.CS_RUN", args=["/tmp/ref_test_int.bin", "BLOCK"], max_delay=5 ) diff --git a/Ref/test/int/test_seq.seq b/Ref/test/int/test_seq.seq index 480ef0b32f..c80b1f5578 100644 --- a/Ref/test/int/test_seq.seq +++ b/Ref/test/int/test_seq.seq @@ -1,6 +1,6 @@ ; A test sequence ; -R00:00:00 cmdDisp.CMD_NO_OP +R00:00:00 Ref.cmdDisp.CMD_NO_OP ; Let's try out some commands with arguments -R00:00:01.050 cmdDisp.CMD_NO_OP_STRING "Awesome string!"; \ No newline at end of file +R00:00:01.050 Ref.cmdDisp.CMD_NO_OP_STRING "Awesome string!"; \ No newline at end of file diff --git a/Svc/ActiveLogger/ActiveLoggerImpl.cpp b/Svc/ActiveLogger/ActiveLoggerImpl.cpp index 8bd9ad7711..81afebcb23 100644 --- a/Svc/ActiveLogger/ActiveLoggerImpl.cpp +++ b/Svc/ActiveLogger/ActiveLoggerImpl.cpp @@ -40,13 +40,6 @@ namespace Svc { ActiveLoggerImpl::~ActiveLoggerImpl() { } - void ActiveLoggerImpl::init( - NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ - ) { - ActiveLoggerComponentBase::init(queueDepth,instance); - } - void ActiveLoggerImpl::LogRecv_handler(NATIVE_INT_TYPE portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::LogBuffer &args) { // make sure ID is not zero. Zero is reserved for ID filter. diff --git a/Svc/ActiveLogger/ActiveLoggerImpl.hpp b/Svc/ActiveLogger/ActiveLoggerImpl.hpp index 7fa37064fd..e7e72593f5 100644 --- a/Svc/ActiveLogger/ActiveLoggerImpl.hpp +++ b/Svc/ActiveLogger/ActiveLoggerImpl.hpp @@ -18,10 +18,6 @@ namespace Svc { public: ActiveLoggerImpl(const char* compName); //!< constructor virtual ~ActiveLoggerImpl(); //!< destructor - void init( - NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ - ); //!< initialization function PROTECTED: PRIVATE: void LogRecv_handler(NATIVE_INT_TYPE portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::LogBuffer &args); diff --git a/Svc/ActiveRateGroup/ActiveRateGroup.cpp b/Svc/ActiveRateGroup/ActiveRateGroup.cpp index bed4355d01..07c7b9dcf5 100644 --- a/Svc/ActiveRateGroup/ActiveRateGroup.cpp +++ b/Svc/ActiveRateGroup/ActiveRateGroup.cpp @@ -44,10 +44,6 @@ namespace Svc { } } - void ActiveRateGroup::init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance) { - ActiveRateGroupComponentBase::init(queueDepth,instance); - } - ActiveRateGroup::~ActiveRateGroup() { } diff --git a/Svc/ActiveRateGroup/ActiveRateGroup.hpp b/Svc/ActiveRateGroup/ActiveRateGroup.hpp index 11e64d8103..979a17643e 100644 --- a/Svc/ActiveRateGroup/ActiveRateGroup.hpp +++ b/Svc/ActiveRateGroup/ActiveRateGroup.hpp @@ -40,16 +40,6 @@ namespace Svc { //! \param compName Name of the component ActiveRateGroup(const char* compName); - //! \brief ActiveRateGroup initialization function - //! - //! The initialization function of the class initializes the member - //! ports and the component base class - //! - //! \param queueDepth Depth of the active component message queue - //! \param instance Identifies the instance of the rate group component - - void init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance); - //! \brief ActiveRateGroup configuration function //! //! The configuration function takes an array of context values to pass to diff --git a/Svc/ActiveTextLogger/ActiveTextLogger.cpp b/Svc/ActiveTextLogger/ActiveTextLogger.cpp index b6af87dda1..0b5a5e8cdf 100644 --- a/Svc/ActiveTextLogger/ActiveTextLogger.cpp +++ b/Svc/ActiveTextLogger/ActiveTextLogger.cpp @@ -26,11 +26,6 @@ namespace Svc { } - void ActiveTextLogger::init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance) - { - ActiveTextLoggerComponentBase::init(queueDepth,instance); - } - // ---------------------------------------------------------------------- // Handlers to implement for typed input ports // ---------------------------------------------------------------------- diff --git a/Svc/ActiveTextLogger/ActiveTextLogger.hpp b/Svc/ActiveTextLogger/ActiveTextLogger.hpp index e7ad9f521d..7895e71341 100644 --- a/Svc/ActiveTextLogger/ActiveTextLogger.hpp +++ b/Svc/ActiveTextLogger/ActiveTextLogger.hpp @@ -39,15 +39,6 @@ namespace Svc { //! virtual ~ActiveTextLogger(); //!< destructor - //! \brief Component initialization routine - //! - //! The initialization function calls the initialization - //! routine for the base class. - //! - //! \param queueDepth the depth of the message queue for the component - //! \param instance: instance identifier. Default: 0. - void init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance = 0); - //! \brief Set log file and max size //! //! This is to create an optional log file to write all the messages to. diff --git a/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.cpp b/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.cpp index 818c0dceed..88e4a40f68 100644 --- a/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.cpp +++ b/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.cpp @@ -54,14 +54,6 @@ namespace Svc { } - void AssertFatalAdapterComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - AssertFatalAdapterComponentBase::init(instance); - } - AssertFatalAdapterComponentImpl :: ~AssertFatalAdapterComponentImpl() { diff --git a/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.hpp b/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.hpp index 61b3f65541..c96a585add 100644 --- a/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.hpp +++ b/Svc/AssertFatalAdapter/AssertFatalAdapterComponentImpl.hpp @@ -33,12 +33,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize object AssertFatalAdapter - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object AssertFatalAdapter //! ~AssertFatalAdapterComponentImpl(); diff --git a/Svc/BufferAccumulator/BufferAccumulator.cpp b/Svc/BufferAccumulator/BufferAccumulator.cpp index bbd84c92e5..3c91691c62 100644 --- a/Svc/BufferAccumulator/BufferAccumulator.cpp +++ b/Svc/BufferAccumulator/BufferAccumulator.cpp @@ -39,11 +39,6 @@ BufferAccumulator :: m_allocatorId(0) { } -void BufferAccumulator ::init(const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance) { - BufferAccumulatorComponentBase::init(queueDepth, instance); -} - BufferAccumulator ::~BufferAccumulator() {} // ---------------------------------------------------------------------- diff --git a/Svc/BufferAccumulator/BufferAccumulator.hpp b/Svc/BufferAccumulator/BufferAccumulator.hpp index a4b8bad2f9..2ac8f93d43 100644 --- a/Svc/BufferAccumulator/BufferAccumulator.hpp +++ b/Svc/BufferAccumulator/BufferAccumulator.hpp @@ -37,7 +37,7 @@ namespace Svc { ~ArrayFIFOBuffer(); void init(Fw::Buffer* const elements, //!< The array elements - NATIVE_UINT_TYPE capacity //!< The capacity + NATIVE_UINT_TYPE capacity //!< The capacity ); //! Enqueue an index. @@ -92,12 +92,6 @@ namespace Svc { const char* const compName /*!< The component name*/ ); - //! Initialize BufferAccumulator instance - //! - void init(const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy BufferAccumulator instance //! ~BufferAccumulator(); diff --git a/Svc/BufferLogger/BufferLogger.cpp b/Svc/BufferLogger/BufferLogger.cpp index bc4942a7c4..69e7bac4f0 100644 --- a/Svc/BufferLogger/BufferLogger.cpp +++ b/Svc/BufferLogger/BufferLogger.cpp @@ -28,15 +28,6 @@ namespace Svc { } - void BufferLogger :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - BufferLoggerComponentBase::init(queueDepth, instance); - } - // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- diff --git a/Svc/BufferLogger/BufferLogger.hpp b/Svc/BufferLogger/BufferLogger.hpp index d5f9d87f6a..f579227b8b 100644 --- a/Svc/BufferLogger/BufferLogger.hpp +++ b/Svc/BufferLogger/BufferLogger.hpp @@ -161,11 +161,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize a BufferLogger object - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); // ---------------------------------------------------------------------- // Public methods diff --git a/Svc/BufferLogger/Commands.fppi b/Svc/BufferLogger/Commands.fppi index 576929eeed..e8b2636adf 100644 --- a/Svc/BufferLogger/Commands.fppi +++ b/Svc/BufferLogger/Commands.fppi @@ -15,7 +15,7 @@ enum LogState { @ Sets the volatile logging state async command BL_SetLogging( - state: LogState + $state: LogState ) \ opcode 0x02 diff --git a/Svc/BufferManager/BufferManagerComponentImpl.cpp b/Svc/BufferManager/BufferManagerComponentImpl.cpp index 192bcf0d59..aa35a32a00 100644 --- a/Svc/BufferManager/BufferManagerComponentImpl.cpp +++ b/Svc/BufferManager/BufferManagerComponentImpl.cpp @@ -42,14 +42,6 @@ namespace Svc { } - void BufferManagerComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - BufferManagerComponentBase::init(instance); - } - BufferManagerComponentImpl :: ~BufferManagerComponentImpl() { diff --git a/Svc/BufferManager/BufferManagerComponentImpl.hpp b/Svc/BufferManager/BufferManagerComponentImpl.hpp index 9d0fdf15a0..c5512755f0 100644 --- a/Svc/BufferManager/BufferManagerComponentImpl.hpp +++ b/Svc/BufferManager/BufferManagerComponentImpl.hpp @@ -68,12 +68,6 @@ namespace Svc const char *const compName /*!< The component name*/ ); - //! Initialize object BufferManager - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - // Defines a buffer bin struct BufferBin { diff --git a/Svc/BufferRepeater/BufferRepeater.cpp b/Svc/BufferRepeater/BufferRepeater.cpp index d081f4bd17..290fdae346 100644 --- a/Svc/BufferRepeater/BufferRepeater.cpp +++ b/Svc/BufferRepeater/BufferRepeater.cpp @@ -23,10 +23,6 @@ BufferRepeater ::BufferRepeater(const char* const compName) : BufferRepeaterComponentBase(compName), m_allocation_failure_response(BufferRepeater::NUM_BUFFER_REPEATER_FAILURE_OPTIONS) {} -void BufferRepeater ::init(const NATIVE_INT_TYPE instance) { - BufferRepeaterComponentBase::init(instance); -} - BufferRepeater ::~BufferRepeater() {} void BufferRepeater ::configure(BufferRepeater::BufferRepeaterFailureOption allocation_failure_response) { diff --git a/Svc/BufferRepeater/BufferRepeater.hpp b/Svc/BufferRepeater/BufferRepeater.hpp index 34873565f1..962b09c53b 100644 --- a/Svc/BufferRepeater/BufferRepeater.hpp +++ b/Svc/BufferRepeater/BufferRepeater.hpp @@ -37,11 +37,6 @@ class BufferRepeater : public BufferRepeaterComponentBase { BufferRepeater(const char* const compName /*!< The component name*/ ); - //! Initialize object BufferRepeater - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object BufferRepeater //! ~BufferRepeater(); diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 37de2e0110..2ab95efc03 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -42,6 +42,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PassiveRateGroup") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PolyDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PrmDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/RateGroupDriver/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StaticMemory/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/") diff --git a/Svc/CmdDispatcher/CommandDispatcherImpl.cpp b/Svc/CmdDispatcher/CommandDispatcherImpl.cpp index c9c7e8e7a9..9d27f6166f 100644 --- a/Svc/CmdDispatcher/CommandDispatcherImpl.cpp +++ b/Svc/CmdDispatcher/CommandDispatcherImpl.cpp @@ -24,13 +24,6 @@ namespace Svc { CommandDispatcherImpl::~CommandDispatcherImpl() { } - void CommandDispatcherImpl::init( - NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ - ) { - CommandDispatcherComponentBase::init(queueDepth); - } - void CommandDispatcherImpl::compCmdReg_handler(NATIVE_INT_TYPE portNum, FwOpcodeType opCode) { // search for an empty slot bool slotFound = false; diff --git a/Svc/CmdDispatcher/CommandDispatcherImpl.hpp b/Svc/CmdDispatcher/CommandDispatcherImpl.hpp index cf96027df2..59688fde7b 100644 --- a/Svc/CmdDispatcher/CommandDispatcherImpl.hpp +++ b/Svc/CmdDispatcher/CommandDispatcherImpl.hpp @@ -39,16 +39,6 @@ namespace Svc { //! //! \param name the component instance name CommandDispatcherImpl(const char* name); - //! \brief Component initialization routine - //! - //! The initialization function calls the initialization - //! routine for the base class. - //! - //! \param queueDepth the depth of the message queue for the component - void init( - NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ - ); //!< initialization function //! \brief Component destructor //! //! The destructor for this component is empty diff --git a/Svc/CmdSequencer/CmdSequencer.fpp b/Svc/CmdSequencer/CmdSequencer.fpp index 52df683856..df55efaa74 100644 --- a/Svc/CmdSequencer/CmdSequencer.fpp +++ b/Svc/CmdSequencer/CmdSequencer.fpp @@ -85,6 +85,9 @@ module Svc { @ Schedule in port async input port schedIn: Svc.Sched + @ Notifies that a sequence has started running + output port seqStartOut: Svc.CmdSeqIn + # ---------------------------------------------------------------------- # Commands # ---------------------------------------------------------------------- diff --git a/Svc/CmdSequencer/CmdSequencerImpl.cpp b/Svc/CmdSequencer/CmdSequencerImpl.cpp index 7e1c84d25b..42050a3535 100644 --- a/Svc/CmdSequencer/CmdSequencerImpl.cpp +++ b/Svc/CmdSequencer/CmdSequencerImpl.cpp @@ -44,11 +44,6 @@ namespace Svc { } - void CmdSequencerComponentImpl::init(const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance) { - CmdSequencerComponentBase::init(queueDepth, instance); - } - void CmdSequencerComponentImpl::setTimeout(const NATIVE_UINT_TYPE timeout) { this->m_timeout = timeout; } @@ -122,6 +117,9 @@ namespace Svc { // Check the step mode. If it is auto, start the sequence if (AUTO == this->m_stepMode) { this->m_runMode = RUNNING; + if(this->isConnected_seqStartOut_OutputPort(0)) { + this->seqStartOut_out(0, this->m_sequence->getStringFileName()); + } this->performCmd_Step(); } @@ -159,7 +157,7 @@ namespace Svc { //! Handler for input port seqRunIn void CmdSequencerComponentImpl::seqRunIn_handler( NATIVE_INT_TYPE portNum, - Fw::String &filename + const Fw::StringBase& filename ) { if (!this->requireRunMode(STOPPED)) { @@ -190,6 +188,9 @@ namespace Svc { // Check the step mode. If it is auto, start the sequence if (AUTO == this->m_stepMode) { this->m_runMode = RUNNING; + if(this->isConnected_seqStartOut_OutputPort(0)) { + this->seqStartOut_out(0, this->m_sequence->getStringFileName()); + } this->performCmd_Step(); } @@ -359,6 +360,9 @@ namespace Svc { this->m_runMode = RUNNING; this->performCmd_Step(); this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName()); + if(this->isConnected_seqStartOut_OutputPort(0)) { + this->seqStartOut_out(0, this->m_sequence->getStringFileName()); + } this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK); } diff --git a/Svc/CmdSequencer/CmdSequencerImpl.hpp b/Svc/CmdSequencer/CmdSequencerImpl.hpp index 6e595000b4..4df41c99b2 100644 --- a/Svc/CmdSequencer/CmdSequencerImpl.hpp +++ b/Svc/CmdSequencer/CmdSequencerImpl.hpp @@ -235,6 +235,10 @@ namespace Svc { //! \return The log file name Fw::LogStringArg& getLogFileName(); + //! Get the normal string file name + //! \return The normal string file name + Fw::String& getStringFileName(); + //! Get the sequence header const Header& getHeader() const; @@ -277,6 +281,9 @@ namespace Svc { //! Copy of file name for events Fw::LogStringArg m_logFileName; + //! Copy of file name for ports + Fw::String m_stringFileName; + //! Serialize buffer to hold the binary sequence data Fw::ExternalSerializeBuffer m_buffer; @@ -515,12 +522,6 @@ namespace Svc { const char* compName //!< The component name ); - //! Initialize a CmdSequencer - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); - //! (Optional) Set a timeout. //! Sequence will quit if a command takes longer than the number of //! seconds in the timeout value. @@ -582,7 +583,7 @@ namespace Svc { //! Handler for input port seqRunIn void seqRunIn_handler( NATIVE_INT_TYPE portNum, //!< The port number - Fw::String &filename //!< The sequence file + const Fw::StringBase& filename //!< The sequence file ); //! Handler for ping port diff --git a/Svc/CmdSequencer/Sequence.cpp b/Svc/CmdSequencer/Sequence.cpp index 235de84c13..30cc2ccb50 100644 --- a/Svc/CmdSequencer/Sequence.cpp +++ b/Svc/CmdSequencer/Sequence.cpp @@ -112,6 +112,7 @@ namespace Svc { { this->m_fileName = fileName; this->m_logFileName = fileName; + this->m_stringFileName = fileName; } Fw::CmdStringArg& CmdSequencerComponentImpl::Sequence :: @@ -126,5 +127,11 @@ namespace Svc { return this->m_logFileName; } + Fw::String& CmdSequencerComponentImpl::Sequence :: + getStringFileName() + { + return this->m_stringFileName; + } + } diff --git a/Svc/CmdSequencer/test/ut/AMPCS.cpp b/Svc/CmdSequencer/test/ut/AMPCS.cpp index 69c2e89b05..a220ee7a4a 100644 --- a/Svc/CmdSequencer/test/ut/AMPCS.cpp +++ b/Svc/CmdSequencer/test/ut/AMPCS.cpp @@ -84,7 +84,7 @@ namespace Svc { 0, file.getName().toChar(), CmdSequencer_FileReadStage::READ_HEADER_SIZE, - Os::FileSystem::INVALID_PATH + Os::FileSystem::DOESNT_EXIST ); // Assert telemetry ASSERT_TLM_SIZE(1); diff --git a/Svc/ComLogger/ComLogger.cpp b/Svc/ComLogger/ComLogger.cpp index 7db091a964..063318d0de 100644 --- a/Svc/ComLogger/ComLogger.cpp +++ b/Svc/ComLogger/ComLogger.cpp @@ -47,16 +47,6 @@ namespace Svc { { } - - void ComLogger :: - init( - NATIVE_INT_TYPE queueDepth, //!< The queue depth - NATIVE_INT_TYPE instance //!< The instance number - ) - { - ComLoggerComponentBase::init(queueDepth, instance); - } - void ComLogger :: init_log_file(const char* incomingFilePrefix, U32 maxFileSize, bool storeBufferLength) { diff --git a/Svc/ComLogger/ComLogger.hpp b/Svc/ComLogger/ComLogger.hpp index 9ce816e17e..2122ed254f 100644 --- a/Svc/ComLogger/ComLogger.hpp +++ b/Svc/ComLogger/ComLogger.hpp @@ -55,11 +55,6 @@ namespace Svc { // CONSTRUCTOR: ComLogger(const char* compName); - void init( - NATIVE_INT_TYPE queueDepth, //!< The queue depth - NATIVE_INT_TYPE instance //!< The instance number - ); - // filePrefix: string to prepend the file name with, ie. "thermal_telemetry" // maxFileSize: the maximum size a file should reach before being closed and a new one opened // storeBufferLength: if true, store the length of each com buffer before storing the buffer itself, diff --git a/Svc/ComQueue/CMakeLists.txt b/Svc/ComQueue/CMakeLists.txt index c1733c668b..5e3d657fd3 100644 --- a/Svc/ComQueue/CMakeLists.txt +++ b/Svc/ComQueue/CMakeLists.txt @@ -23,4 +23,6 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/ComQueueTestMain.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ComQueueTester.cpp" ) + +set(UT_AUTO_HELPERS ON) register_fprime_ut() diff --git a/Svc/ComQueue/ComQueue.cpp b/Svc/ComQueue/ComQueue.cpp index a1fb1c40c2..63124fe6fd 100644 --- a/Svc/ComQueue/ComQueue.cpp +++ b/Svc/ComQueue/ComQueue.cpp @@ -35,10 +35,6 @@ ComQueue ::ComQueue(const char* const compName) ComQueue ::~ComQueue() {} -void ComQueue ::init(const NATIVE_INT_TYPE queueDepth, const NATIVE_INT_TYPE instance) { - ComQueueComponentBase::init(queueDepth, instance); -} - void ComQueue ::cleanup() { // Deallocate memory ignoring error conditions if ((this->m_allocator != nullptr) && (this->m_allocation != nullptr)) { @@ -131,7 +127,7 @@ void ComQueue::configure(QueueConfigurationTable queueConfig, void ComQueue::comQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::ComBuffer& data, U32 context) { // Ensure that the port number of comQueueIn is consistent with the expectation FW_ASSERT(portNum >= 0 && portNum < COM_PORT_COUNT, portNum); - this->enqueue(portNum, QueueType::COM_QUEUE, reinterpret_cast(&data), sizeof(Fw::ComBuffer)); + (void)this->enqueue(portNum, QueueType::COM_QUEUE, reinterpret_cast(&data), sizeof(Fw::ComBuffer)); } void ComQueue::buffQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) { @@ -139,7 +135,11 @@ void ComQueue::buffQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fw // Ensure that the port number of buffQueueIn is consistent with the expectation FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, portNum); FW_ASSERT(queueNum < TOTAL_PORT_COUNT); - this->enqueue(queueNum, QueueType::BUFFER_QUEUE, reinterpret_cast(&fwBuffer), sizeof(Fw::Buffer)); + bool status = + this->enqueue(queueNum, QueueType::BUFFER_QUEUE, reinterpret_cast(&fwBuffer), sizeof(Fw::Buffer)); + if (!status) { + this->deallocate_out(portNum, fwBuffer); + } } void ComQueue::comStatusIn_handler(const NATIVE_INT_TYPE portNum, Fw::Success& condition) { @@ -181,29 +181,45 @@ void ComQueue::run_handler(const NATIVE_INT_TYPE portNum, U32 context) { this->tlmWrite_buffQueueDepth(buffQueueDepth); } +// ---------------------------------------------------------------------- +// Hook implementations for typed async input ports +// ---------------------------------------------------------------------- + +void ComQueue::buffQueueIn_overflowHook(FwIndexType portNum, Fw::Buffer& fwBuffer) { + FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, portNum); + this->deallocate_out(portNum, fwBuffer); +} + // ---------------------------------------------------------------------- // Private helper methods // ---------------------------------------------------------------------- -void ComQueue::enqueue(const FwIndexType queueNum, QueueType queueType, const U8* data, const FwSizeType size) { +bool ComQueue::enqueue(const FwIndexType queueNum, QueueType queueType, const U8* data, const FwSizeType size) { // Enqueue the given message onto the matching queue. When no space is available then emit the queue overflow event, // set the appropriate throttle, and move on. Will assert if passed a message for a depth 0 queue. const FwSizeType expectedSize = (queueType == QueueType::COM_QUEUE) ? sizeof(Fw::ComBuffer) : sizeof(Fw::Buffer); const FwIndexType portNum = queueNum - ((queueType == QueueType::COM_QUEUE) ? 0 : COM_PORT_COUNT); + bool rvStatus = true; FW_ASSERT( expectedSize == size, static_cast(size), static_cast(expectedSize)); FW_ASSERT(portNum >= 0, portNum); Fw::SerializeStatus status = this->m_queues[queueNum].enqueue(data, size); - if (status == Fw::FW_SERIALIZE_NO_ROOM_LEFT && !this->m_throttle[queueNum]) { - this->log_WARNING_HI_QueueOverflow(queueType, static_cast(portNum)); - this->m_throttle[queueNum] = true; + if (status == Fw::FW_SERIALIZE_NO_ROOM_LEFT) { + if (!this->m_throttle[queueNum]) { + this->log_WARNING_HI_QueueOverflow(queueType, static_cast(portNum)); + this->m_throttle[queueNum] = true; + } + + rvStatus = false; } // When the component is already in READY state process the queue to send out the next available message immediately if (this->m_state == READY) { this->processQueue(); } + + return rvStatus; } void ComQueue::sendComBuffer(Fw::ComBuffer& comBuffer) { diff --git a/Svc/ComQueue/ComQueue.fpp b/Svc/ComQueue/ComQueue.fpp index 43cda64368..a45ec081a5 100644 --- a/Svc/ComQueue/ComQueue.fpp +++ b/Svc/ComQueue/ComQueue.fpp @@ -4,7 +4,7 @@ module Svc { @ Array of queue depths for Fw::Com types array ComQueueDepth = [ComQueueComPorts] U32 - + @ Array of queue depths for Fw::Buffer types array BuffQueueDepth = [ComQueueBufferPorts] U32 @@ -22,6 +22,9 @@ module Svc { @ Fw::Buffer output port output port buffQueueSend: Fw.BufferSend + @ Port for deallocating Fw::Buffer on queue overflow + output port deallocate: Fw.BufferSend + @ Port for receiving the status signal async input port comStatusIn: Fw.SuccessCondition @@ -29,7 +32,7 @@ module Svc { async input port comQueueIn: [ComQueueComPorts] Fw.Com drop @ Port array for receiving Fw::Buffers - async input port buffQueueIn: [ComQueueBufferPorts] Fw.BufferSend drop + async input port buffQueueIn: [ComQueueBufferPorts] Fw.BufferSend hook @ Port for scheduling telemetry output async input port run: Svc.Sched drop diff --git a/Svc/ComQueue/ComQueue.hpp b/Svc/ComQueue/ComQueue.hpp index 550be00c98..d1e22040e8 100644 --- a/Svc/ComQueue/ComQueue.hpp +++ b/Svc/ComQueue/ComQueue.hpp @@ -99,12 +99,6 @@ class ComQueue : public ComQueueComponentBase { ComQueue(const char* const compName /*!< The component name */ ); - //! Initialize object ComQueue - //! - void init(const NATIVE_INT_TYPE queueDepth, /*!< The queue depth */ - const NATIVE_INT_TYPE instance = 0 /*!< The instance number */ - ); - //! Destroy object ComQueue //! ~ComQueue(); @@ -151,13 +145,23 @@ class ComQueue : public ComQueueComponentBase { U32 context /*!component.m_queue.getQueueSize(); + QueueType overflow_type; + NATIVE_INT_TYPE portNum; + + // fill the queue + for (NATIVE_INT_TYPE msgCount = 0; msgCount < msgCountMax; msgCount++) { + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + ASSERT_EQ(overflow_type, QueueType::BUFFER_QUEUE); + } + + // send one more to overflow the queue + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + + ASSERT_from_deallocate_SIZE(1); + ASSERT_from_deallocate(0, buffer); + + // send another + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + + ASSERT_from_deallocate_SIZE(2); + ASSERT_from_deallocate(0, buffer); + ASSERT_from_deallocate(1, buffer); + + component.cleanup(); +} + void ComQueueTester ::testReadyFirst() { U8 data[BUFFER_LENGTH] = {0xde, 0xad, 0xbe}; Fw::ComBuffer comBuffer(&data[0], sizeof(data)); @@ -283,45 +330,4 @@ void ComQueueTester ::from_comQueueSend_handler(const NATIVE_INT_TYPE portNum, F // Helper methods // ---------------------------------------------------------------------- -void ComQueueTester ::connectPorts() { - // buffQueueIn - for (NATIVE_INT_TYPE i = 0; i < ComQueue::BUFFER_PORT_COUNT; ++i) { - this->connect_to_buffQueueIn(i, this->component.get_buffQueueIn_InputPort(i)); - } - - // comQueueIn - for (NATIVE_INT_TYPE i = 0; i < ComQueue::COM_PORT_COUNT; ++i) { - this->connect_to_comQueueIn(i, this->component.get_comQueueIn_InputPort(i)); - } - - // comStatusIn - this->connect_to_comStatusIn(0, this->component.get_comStatusIn_InputPort(0)); - - // run - this->connect_to_run(0, this->component.get_run_InputPort(0)); - - // Log - this->component.set_Log_OutputPort(0, this->get_from_Log(0)); - - // LogText - this->component.set_LogText_OutputPort(0, this->get_from_LogText(0)); - - // Time - this->component.set_Time_OutputPort(0, this->get_from_Time(0)); - - // Tlm - this->component.set_Tlm_OutputPort(0, this->get_from_Tlm(0)); - - // buffQueueSend - this->component.set_buffQueueSend_OutputPort(0, this->get_from_buffQueueSend(0)); - - // comQueueSend - this->component.set_comQueueSend_OutputPort(0, this->get_from_comQueueSend(0)); -} - -void ComQueueTester ::initComponents() { - this->init(); - this->component.init(QUEUE_DEPTH, INSTANCE); -} - } // end namespace Svc diff --git a/Svc/ComQueue/test/ut/ComQueueTester.hpp b/Svc/ComQueue/test/ut/ComQueueTester.hpp index 5149edf8c8..41da0db4fb 100644 --- a/Svc/ComQueue/test/ut/ComQueueTester.hpp +++ b/Svc/ComQueue/test/ut/ComQueueTester.hpp @@ -14,6 +14,19 @@ namespace Svc { class ComQueueTester : public ComQueueGTestBase { + + public: + + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + // Instance ID supplied to the component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_ID = 0; + + // Queue depth supplied to the component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_QUEUE_DEPTH = 10; + private: // ---------------------------------------------------------------------- // Construction and destruction @@ -38,7 +51,10 @@ class ComQueueTester : public ComQueueGTestBase { // ---------------------------------------------------------------------- void configure(); - void sendByQueueNumber(NATIVE_INT_TYPE queueNumber, NATIVE_INT_TYPE& portNum, QueueType& queueType); + void sendByQueueNumber(Fw::Buffer& buffer, + NATIVE_INT_TYPE queueNumber, + NATIVE_INT_TYPE& portNum, + QueueType& queueType); void emitOne(); @@ -57,7 +73,9 @@ class ComQueueTester : public ComQueueGTestBase { void testPrioritySend(); - void testQueueOverflow(); + void testExternalQueueOverflow(); + + void testInternalQueueOverflow(); void testReadyFirst(); diff --git a/Svc/ComSplitter/ComSplitter.cpp b/Svc/ComSplitter/ComSplitter.cpp index 92f170e2d6..3e3a2a7f80 100644 --- a/Svc/ComSplitter/ComSplitter.cpp +++ b/Svc/ComSplitter/ComSplitter.cpp @@ -26,12 +26,6 @@ namespace Svc { } - void ComSplitter :: - init(NATIVE_INT_TYPE instance) - { - ComSplitterComponentBase::init(instance); - } - // ---------------------------------------------------------------------- // Handler implementations // ---------------------------------------------------------------------- diff --git a/Svc/ComSplitter/ComSplitter.hpp b/Svc/ComSplitter/ComSplitter.hpp index 4a51d181d2..1b99d02767 100644 --- a/Svc/ComSplitter/ComSplitter.hpp +++ b/Svc/ComSplitter/ComSplitter.hpp @@ -32,8 +32,6 @@ namespace Svc { ~ComSplitter(); - void init(NATIVE_INT_TYPE instance); - // ---------------------------------------------------------------------- // Handler implementations // ---------------------------------------------------------------------- diff --git a/Svc/ComStub/ComStub.cpp b/Svc/ComStub/ComStub.cpp index 1cf361fcba..0fc48fb818 100644 --- a/Svc/ComStub/ComStub.cpp +++ b/Svc/ComStub/ComStub.cpp @@ -16,10 +16,6 @@ namespace Svc { ComStub::ComStub(const char* const compName) : ComStubComponentBase(compName), m_reinitialize(true) {} -void ComStub::init(const NATIVE_INT_TYPE instance) { - ComStubComponentBase::init(instance); -} - ComStub::~ComStub() {} // ---------------------------------------------------------------------- diff --git a/Svc/ComStub/ComStub.hpp b/Svc/ComStub/ComStub.hpp index f75b634660..5c678cf998 100644 --- a/Svc/ComStub/ComStub.hpp +++ b/Svc/ComStub/ComStub.hpp @@ -23,11 +23,6 @@ class ComStub : public ComStubComponentBase { ComStub(const char* const compName /*!< The component name*/ ); - //! Initialize object ComStub - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object ComStub //! ~ComStub() override; diff --git a/Svc/Deframer/Deframer.cpp b/Svc/Deframer/Deframer.cpp index 8ed542dec5..2f697da8b6 100644 --- a/Svc/Deframer/Deframer.cpp +++ b/Svc/Deframer/Deframer.cpp @@ -45,10 +45,6 @@ Deframer ::Deframer(const char* const compName) : (void) memset(m_pollBuffer, 0, sizeof m_pollBuffer); } -void Deframer ::init(const NATIVE_INT_TYPE instance) { - DeframerComponentBase::init(instance); -} - Deframer ::~Deframer() {} void Deframer ::setup(DeframingProtocol& protocol) { diff --git a/Svc/Deframer/Deframer.hpp b/Svc/Deframer/Deframer.hpp index 56b75911ea..b0d3a78653 100644 --- a/Svc/Deframer/Deframer.hpp +++ b/Svc/Deframer/Deframer.hpp @@ -50,11 +50,6 @@ class Deframer : const char* const compName //!< The component name ); - //! Initialize Deframer instance - void init( - const NATIVE_INT_TYPE instance = 0 //!< The instance number - ); - //! Destroy Deframer instance ~Deframer(); diff --git a/Svc/DpCatalog/DpCatalog.cpp b/Svc/DpCatalog/DpCatalog.cpp index c306dc2773..6954532a93 100644 --- a/Svc/DpCatalog/DpCatalog.cpp +++ b/Svc/DpCatalog/DpCatalog.cpp @@ -129,21 +129,25 @@ namespace Svc { for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) { // read in each directory and keep track of total this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]); - U32 filesRead = 0; + FwSizeType filesRead = 0; U32 pendingFiles = 0; U64 pendingDpBytes = 0; - Os::FileSystem::Status fsStat = - Os::FileSystem::readDirectory( - this->m_directories[dir].toChar(), - static_cast(this->m_numDpSlots - totalFiles), - this->m_fileList, - filesRead + Os::Directory dpDir; + Os::Directory::Status status = dpDir.open(this->m_directories[dir].toChar(), Os::Directory::OpenMode::READ); + if (status != Os::Directory::OP_OK) { + this->log_WARNING_HI_DirectoryOpenError( + this->m_directories[dir], + status ); - if (fsStat != Os::FileSystem::OP_OK) { + return Fw::CmdResponse::EXECUTION_ERROR; + } + status = dpDir.readDirectory(this->m_fileList, (this->m_numDpSlots - totalFiles), filesRead); + + if (status != Os::Directory::OP_OK) { this->log_WARNING_HI_DirectoryOpenError( this->m_directories[dir], - fsStat + status ); return Fw::CmdResponse::EXECUTION_ERROR; } diff --git a/Svc/DpCatalog/DpCatalog.fpp b/Svc/DpCatalog/DpCatalog.fpp index b5d548505e..06dc6d6702 100644 --- a/Svc/DpCatalog/DpCatalog.fpp +++ b/Svc/DpCatalog/DpCatalog.fpp @@ -21,7 +21,7 @@ module Svc { tSub: U32 $priority: U32 $size: U64 - state: Fw.DpState + $state: Fw.DpState } diff --git a/Svc/FatalHandler/FatalHandlerComponentCommonImpl.cpp b/Svc/FatalHandler/FatalHandlerComponentCommonImpl.cpp index 42bd6ec9e4..4df96c0e00 100644 --- a/Svc/FatalHandler/FatalHandlerComponentCommonImpl.cpp +++ b/Svc/FatalHandler/FatalHandlerComponentCommonImpl.cpp @@ -28,14 +28,6 @@ namespace Svc { } - void FatalHandlerComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - FatalHandlerComponentBase::init(instance); - } - FatalHandlerComponentImpl :: ~FatalHandlerComponentImpl() { diff --git a/Svc/FatalHandler/FatalHandlerComponentImpl.hpp b/Svc/FatalHandler/FatalHandlerComponentImpl.hpp index 84dde229df..7333a0f15d 100644 --- a/Svc/FatalHandler/FatalHandlerComponentImpl.hpp +++ b/Svc/FatalHandler/FatalHandlerComponentImpl.hpp @@ -33,12 +33,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize object FatalHandler - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object FatalHandler //! ~FatalHandlerComponentImpl(); diff --git a/Svc/FileDownlink/FileDownlink.cpp b/Svc/FileDownlink/FileDownlink.cpp index baac26209a..ff3622188b 100644 --- a/Svc/FileDownlink/FileDownlink.cpp +++ b/Svc/FileDownlink/FileDownlink.cpp @@ -43,15 +43,6 @@ namespace Svc { { } - void FileDownlink :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - FileDownlinkComponentBase::init(queueDepth, instance); - } - void FileDownlink :: configure( U32 timeout, diff --git a/Svc/FileDownlink/FileDownlink.hpp b/Svc/FileDownlink/FileDownlink.hpp index efc34ec95c..4d623868bc 100644 --- a/Svc/FileDownlink/FileDownlink.hpp +++ b/Svc/FileDownlink/FileDownlink.hpp @@ -269,13 +269,6 @@ namespace Svc { const char *const compName //!< The component name ); - //! Initialize object FileDownlink - //! - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); - //! Configure FileDownlink component //! void configure( diff --git a/Svc/FileManager/FileManager.cpp b/Svc/FileManager/FileManager.cpp index 583056237d..ebca6f3702 100644 --- a/Svc/FileManager/FileManager.cpp +++ b/Svc/FileManager/FileManager.cpp @@ -34,15 +34,6 @@ namespace Svc { } - void FileManager :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - FileManagerComponentBase::init(queueDepth, instance); - } - FileManager :: ~FileManager() { @@ -62,8 +53,9 @@ namespace Svc { { Fw::LogStringArg logStringDirName(dirName.toChar()); this->log_ACTIVITY_HI_CreateDirectoryStarted(logStringDirName); + bool errorIfDirExists = true; const Os::FileSystem::Status status = - Os::FileSystem::createDirectory(dirName.toChar()); + Os::FileSystem::createDirectory(dirName.toChar(), errorIfDirExists); if (status != Os::FileSystem::OP_OK) { this->log_WARNING_HI_DirectoryCreateError( logStringDirName, diff --git a/Svc/FileManager/FileManager.hpp b/Svc/FileManager/FileManager.hpp index 69baf655f5..a8273ed205 100644 --- a/Svc/FileManager/FileManager.hpp +++ b/Svc/FileManager/FileManager.hpp @@ -34,13 +34,6 @@ namespace Svc { const char *const compName //!< The component name ); - //! Initialize object FileManager - //! - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); - //! Destroy object FileManager //! ~FileManager(); diff --git a/Svc/FileManager/test/ut/FileManagerTester.cpp b/Svc/FileManager/test/ut/FileManagerTester.cpp index 647dc2a754..04d4f32bda 100644 --- a/Svc/FileManager/test/ut/FileManagerTester.cpp +++ b/Svc/FileManager/test/ut/FileManagerTester.cpp @@ -159,7 +159,7 @@ namespace Svc { 0, "file1", "file2", - Os::FileSystem::INVALID_PATH + Os::FileSystem::DOESNT_EXIST ); } @@ -214,7 +214,7 @@ namespace Svc { ASSERT_EVENTS_DirectoryRemoveError( 0, "test_dir", - Os::FileSystem::INVALID_PATH + Os::FileSystem::DOESNT_EXIST ); } @@ -269,7 +269,7 @@ namespace Svc { ASSERT_EVENTS_FileRemoveError( 0, "test_file", - Os::FileSystem::INVALID_PATH + Os::FileSystem::DOESNT_EXIST ); } @@ -415,7 +415,7 @@ namespace Svc { 0, "file1", "file2", - Os::FileSystem::INVALID_PATH + Os::FileSystem::DOESNT_EXIST ); } diff --git a/Svc/FileUplink/FileUplink.cpp b/Svc/FileUplink/FileUplink.cpp index 3db5aa2f68..2eea06d78a 100644 --- a/Svc/FileUplink/FileUplink.cpp +++ b/Svc/FileUplink/FileUplink.cpp @@ -32,15 +32,6 @@ namespace Svc { } - void FileUplink :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE instance - ) - { - FileUplinkComponentBase::init(queueDepth, instance); - } - FileUplink :: ~FileUplink() { diff --git a/Svc/FileUplink/FileUplink.hpp b/Svc/FileUplink/FileUplink.hpp index 878daaf34e..ce3b6793b5 100644 --- a/Svc/FileUplink/FileUplink.hpp +++ b/Svc/FileUplink/FileUplink.hpp @@ -200,13 +200,6 @@ namespace Svc { const char *const name //!< The component name ); - //! Initialize object FileUplink - //! - void init( - const NATIVE_INT_TYPE queueDepth, //!< The queue depth - const NATIVE_INT_TYPE instance //!< The instance number - ); - //! Destroy object FileUplink //! ~FileUplink(); diff --git a/Svc/Framer/Framer.cpp b/Svc/Framer/Framer.cpp index 0061be08b5..eb63ddc898 100644 --- a/Svc/Framer/Framer.cpp +++ b/Svc/Framer/Framer.cpp @@ -24,10 +24,6 @@ namespace Svc { Framer ::Framer(const char* const compName) : FramerComponentBase(compName), FramingProtocolInterface(), m_protocol(nullptr), m_frame_sent(false) {} -void Framer ::init(const NATIVE_INT_TYPE instance) { - FramerComponentBase::init(instance); -} - Framer ::~Framer() {} void Framer ::setup(FramingProtocol& protocol) { diff --git a/Svc/Framer/Framer.hpp b/Svc/Framer/Framer.hpp index 88f09af8c9..3babf48fc2 100644 --- a/Svc/Framer/Framer.hpp +++ b/Svc/Framer/Framer.hpp @@ -39,11 +39,6 @@ class Framer : public FramerComponentBase, public FramingProtocolInterface { Framer(const char* const compName /*!< The component name*/ ); - //! Initialize object Framer - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! \brief Setup this component with a supplied framing protocol //! void setup(FramingProtocol& protocol /*!< Protocol used in framing */); diff --git a/Svc/GenericHub/GenericHubComponentImpl.cpp b/Svc/GenericHub/GenericHubComponentImpl.cpp index 9a21b04184..c794f37305 100644 --- a/Svc/GenericHub/GenericHubComponentImpl.cpp +++ b/Svc/GenericHub/GenericHubComponentImpl.cpp @@ -26,10 +26,6 @@ namespace Svc { GenericHubComponentImpl ::GenericHubComponentImpl(const char* const compName) : GenericHubComponentBase(compName) {} -void GenericHubComponentImpl ::init(const NATIVE_INT_TYPE instance) { - GenericHubComponentBase::init(instance); -} - GenericHubComponentImpl ::~GenericHubComponentImpl() {} void GenericHubComponentImpl ::send_data(const HubType type, diff --git a/Svc/GenericHub/GenericHubComponentImpl.hpp b/Svc/GenericHub/GenericHubComponentImpl.hpp index f53057af85..1c683dcb3f 100644 --- a/Svc/GenericHub/GenericHubComponentImpl.hpp +++ b/Svc/GenericHub/GenericHubComponentImpl.hpp @@ -42,11 +42,6 @@ class GenericHubComponentImpl : public GenericHubComponentBase { GenericHubComponentImpl(const char* const compName /*!< The component name*/ ); - //! Initialize object GenericHub - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object GenericHub //! ~GenericHubComponentImpl(); diff --git a/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp b/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp index 3cf79ae3d9..75557354f3 100644 --- a/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp +++ b/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp @@ -34,12 +34,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize object LinuxTimer - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object LinuxTimer //! ~LinuxTimerComponentImpl(); diff --git a/Svc/LinuxTimer/LinuxTimerComponentImplCommon.cpp b/Svc/LinuxTimer/LinuxTimerComponentImplCommon.cpp index ec9158262b..edc1f9ee57 100644 --- a/Svc/LinuxTimer/LinuxTimerComponentImplCommon.cpp +++ b/Svc/LinuxTimer/LinuxTimerComponentImplCommon.cpp @@ -29,14 +29,6 @@ namespace Svc { } - void LinuxTimerComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - LinuxTimerComponentBase::init(instance); - } - LinuxTimerComponentImpl :: ~LinuxTimerComponentImpl() { diff --git a/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImpl.hpp b/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImpl.hpp index 7e1a4b6b59..7ecda41f0e 100644 --- a/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImpl.hpp +++ b/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImpl.hpp @@ -11,7 +11,6 @@ namespace Svc { // Only called by derived class ConsoleTextLoggerImpl(const char* compName); - void init(NATIVE_INT_TYPE instanceId = 0); ~ConsoleTextLoggerImpl(); private: diff --git a/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImplCommon.cpp b/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImplCommon.cpp index c7c0ed0991..e30b9bc4a9 100644 --- a/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImplCommon.cpp +++ b/Svc/PassiveConsoleTextLogger/ConsoleTextLoggerImplCommon.cpp @@ -9,10 +9,6 @@ namespace Svc { PassiveTextLoggerComponentBase(compName) { } - void ConsoleTextLoggerImpl::init(NATIVE_INT_TYPE instanceId) { - PassiveTextLoggerComponentBase::init(instanceId); - } - ConsoleTextLoggerImpl::~ConsoleTextLoggerImpl() {} void ConsoleTextLoggerImpl::TextLogger_handler(NATIVE_INT_TYPE portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::TextLogString &text) { diff --git a/Svc/PrmDb/PrmDbImpl.cpp b/Svc/PrmDb/PrmDbImpl.cpp index 8b6b0b11ee..0281803564 100644 --- a/Svc/PrmDb/PrmDbImpl.cpp +++ b/Svc/PrmDb/PrmDbImpl.cpp @@ -50,10 +50,6 @@ namespace Svc { this->m_fileName = file; } - void PrmDbImpl::init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance) { - PrmDbComponentBase::init(queueDepth,instance); - } - void PrmDbImpl::clearDb() { for (I32 entry = 0; entry < PRMDB_NUM_DB_ENTRIES; entry++) { this->m_db[entry].used = false; diff --git a/Svc/PrmDb/PrmDbImpl.hpp b/Svc/PrmDb/PrmDbImpl.hpp index 3016849558..5ea7cd0edf 100644 --- a/Svc/PrmDb/PrmDbImpl.hpp +++ b/Svc/PrmDb/PrmDbImpl.hpp @@ -41,16 +41,6 @@ namespace Svc { //! \param name component instance name PrmDbImpl(const char* name); - //! \brief PrmDb initialization function - //! - //! The initialization function for the component creates the message - //! queue and initializes the component base classes. - //! - //! \param queueDepth queue depth for messages - //! \param instance instance of component, if more than one is needed. - - void init(NATIVE_INT_TYPE queueDepth, NATIVE_INT_TYPE instance); - //! \brief PrmDb configure method //! //! The configure method stores the file name for opening later. diff --git a/Svc/Seq/Seq.fpp b/Svc/Seq/Seq.fpp index 66a5d9888c..1caf1ce037 100644 --- a/Svc/Seq/Seq.fpp +++ b/Svc/Seq/Seq.fpp @@ -2,7 +2,7 @@ module Svc { @ Port to request a sequence be run port CmdSeqIn( - ref filename: Fw.String @< The sequence file + filename: string size 240 @< The sequence file ) @ Port to cancel a sequence diff --git a/Svc/SeqDispatcher/CMakeLists.txt b/Svc/SeqDispatcher/CMakeLists.txt new file mode 100644 index 0000000000..bb92108919 --- /dev/null +++ b/Svc/SeqDispatcher/CMakeLists.txt @@ -0,0 +1,24 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# UT_SOURCE_FILES: list of source files for unit tests +# +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher.fpp" + "${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher.cpp" +) + +register_fprime_module() + +### UTS ### +set(UT_AUTO_HELPERS ON) + +set(UT_SOURCE_FILES + "${FPRIME_FRAMEWORK_PATH}/Svc/SeqDispatcher/SeqDispatcher.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/SeqDispatcherTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/SeqDispatcherTestMain.cpp" +) +register_fprime_ut() diff --git a/Svc/SeqDispatcher/SeqDispatcher.cpp b/Svc/SeqDispatcher/SeqDispatcher.cpp new file mode 100644 index 0000000000..c55bda62c0 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.cpp @@ -0,0 +1,186 @@ +// ====================================================================== +// \title SeqDispatcher.cpp +// \author zimri.leisher +// \brief cpp file for SeqDispatcher component implementation class +// ====================================================================== + +#include + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- + +SeqDispatcher ::SeqDispatcher(const char* const compName) + : SeqDispatcherComponentBase(compName) {} + +SeqDispatcher ::~SeqDispatcher() {} + +FwIndexType SeqDispatcher::getNextAvailableSequencerIdx() { + for (FwIndexType i = 0; i < SeqDispatcherSequencerPorts; i++) { + if (this->isConnected_seqRunOut_OutputPort(i) && + this->m_entryTable[i].state == SeqDispatcher_CmdSequencerState::AVAILABLE) { + return i; + } + } + return -1; +} + +void SeqDispatcher::runSequence(FwIndexType sequencerIdx, + const Fw::StringBase& fileName, + Fw::Wait block) { + // this function is only designed for internal usage + // we can guarantee it cannot be called with input that would fail + FW_ASSERT(sequencerIdx >= 0 && sequencerIdx < SeqDispatcherSequencerPorts, + sequencerIdx); + FW_ASSERT(this->isConnected_seqRunOut_OutputPort(sequencerIdx)); + FW_ASSERT(this->m_entryTable[sequencerIdx].state == + SeqDispatcher_CmdSequencerState::AVAILABLE, + this->m_entryTable[sequencerIdx].state); + + if (block == Fw::Wait::NO_WAIT) { + this->m_entryTable[sequencerIdx].state = + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK; + } else { + this->m_entryTable[sequencerIdx].state = + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK; + } + + this->m_sequencersAvailable--; + this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable); + this->m_entryTable[sequencerIdx].sequenceRunning = fileName; + + this->m_dispatchedCount++; + this->tlmWrite_dispatchedCount(this->m_dispatchedCount); + this->seqRunOut_out(sequencerIdx, + this->m_entryTable[sequencerIdx].sequenceRunning); +} + +void SeqDispatcher::seqStartIn_handler( + NATIVE_INT_TYPE portNum, //!< The port number + const Fw::StringBase& fileName //!< The sequence file name +) { + FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, portNum); + if (this->m_entryTable[portNum].state == + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK || + this->m_entryTable[portNum].state == + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) { + // we were aware of this sequencer running a sequence + if (this->m_entryTable[portNum].sequenceRunning != fileName) { + // uh oh. entry table is wrong + // let's just update it to be correct. nothing we can do about + // it except raise a warning and update our state + this->log_WARNING_HI_ConflictingSequenceStarted(static_cast(portNum), fileName, this->m_entryTable[portNum].sequenceRunning); + this->m_entryTable[portNum].sequenceRunning = fileName; + } + } else { + // we were not aware that this sequencer was running. ground must have + // directly commanded that specific sequencer + + // warn because this may be unintentional + this->log_WARNING_LO_UnexpectedSequenceStarted(static_cast(portNum), fileName); + + // update the state + this->m_entryTable[portNum].state = + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK; + this->m_entryTable[portNum].sequenceRunning = fileName; + this->m_sequencersAvailable--; + this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable); + } +} + +void SeqDispatcher::seqDoneIn_handler( + NATIVE_INT_TYPE portNum, //!< The port number + FwOpcodeType opCode, //!< Command Op Code + U32 cmdSeq, //!< Command Sequence + const Fw::CmdResponse& response //!< The command response argument +) { + FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, portNum); + if (this->m_entryTable[portNum].state != + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK && + this->m_entryTable[portNum].state != + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) { + // this sequencer was not running a sequence that we were aware of. + + // we should have caught this in seqStartIn and updated the state + // accordingly, but somehow we didn't? very sad and shouldn't happen + + // anyways, don't have to do anything cuz now that this seq we didn't know + // about is done, the sequencer is available again (which is its current + // state in our internal entry table already) + this->log_WARNING_LO_UnknownSequenceFinished(static_cast(portNum)); + } else { + // ok, a sequence has finished that we knew about + if (this->m_entryTable[portNum].state == + SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK) { + // we need to give a cmd response cuz some other sequence is being blocked + // by this + this->cmdResponse_out(this->m_entryTable[portNum].opCode, + this->m_entryTable[portNum].cmdSeq, response); + + if (response == Fw::CmdResponse::EXECUTION_ERROR) { + // dispatched sequence errored + this->m_errorCount++; + this->tlmWrite_errorCount(this->m_errorCount); + } + } + } + + // all command responses mean the sequence is no longer running + // so component should be available + this->m_entryTable[portNum].state = SeqDispatcher_CmdSequencerState::AVAILABLE; + this->m_entryTable[portNum].sequenceRunning = ""; + this->m_sequencersAvailable++; + this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable); +} + +//! Handler for input port seqRunIn +void SeqDispatcher::seqRunIn_handler(NATIVE_INT_TYPE portNum, + const Fw::StringBase& fileName) { + FwIndexType idx = this->getNextAvailableSequencerIdx(); + // no available sequencers + if (idx == -1) { + this->log_WARNING_HI_NoAvailableSequencers(); + return; + } + + this->runSequence(idx, fileName, Fw::Wait::NO_WAIT); +} +// ---------------------------------------------------------------------- +// Command handler implementations +// ---------------------------------------------------------------------- + +void SeqDispatcher ::RUN_cmdHandler(const FwOpcodeType opCode, + const U32 cmdSeq, + const Fw::CmdStringArg& fileName, + Fw::Wait block) { + FwIndexType idx = this->getNextAvailableSequencerIdx(); + // no available sequencers + if (idx == -1) { + this->log_WARNING_HI_NoAvailableSequencers(); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + return; + } + + this->runSequence(idx, fileName, block); + + if (block == Fw::Wait::NO_WAIT) { + // return instantly + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + } else { + // otherwise don't return a response yet. just save the opCode and cmdSeq + // so we can return a response later + this->m_entryTable[idx].opCode = opCode; + this->m_entryTable[idx].cmdSeq = cmdSeq; + } +} + +void SeqDispatcher::LOG_STATUS_cmdHandler( + const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq) { /*!< The command sequence number*/ + for(FwIndexType idx = 0; idx < SeqDispatcherSequencerPorts; idx++) { + this->log_ACTIVITY_LO_LogSequencerStatus(static_cast(idx), this->m_entryTable[idx].state, Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning)); + } +} +} // end namespace components diff --git a/Svc/SeqDispatcher/SeqDispatcher.fpp b/Svc/SeqDispatcher/SeqDispatcher.fpp new file mode 100644 index 0000000000..3fc6d9702f --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.fpp @@ -0,0 +1,55 @@ +module Svc { + @ Dispatches command sequences to available command sequencers + active component SeqDispatcher { + + enum CmdSequencerState { + AVAILABLE = 0 + RUNNING_SEQUENCE_BLOCK = 1 + RUNNING_SEQUENCE_NO_BLOCK = 2 + } + + include "SeqDispatcherCommands.fppi" + include "SeqDispatcherTelemetry.fppi" + include "SeqDispatcherEvents.fppi" + + @ Dispatches a sequence to the first available command sequencer + async input port seqRunIn: Svc.CmdSeqIn + + output port seqRunOut: [SeqDispatcherSequencerPorts] Svc.CmdSeqIn + + @ Called by a command sequencer whenever it has finished any sequence + async input port seqDoneIn: [SeqDispatcherSequencerPorts] Fw.CmdResponse + + @ Called by cmdsequencer whenever it starts any sequence + async input port seqStartIn: [SeqDispatcherSequencerPorts] Svc.CmdSeqIn + + match seqRunOut with seqDoneIn + + match seqRunOut with seqStartIn + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + } +} \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcher.hpp b/Svc/SeqDispatcher/SeqDispatcher.hpp new file mode 100644 index 0000000000..9c8b9f6806 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.hpp @@ -0,0 +1,95 @@ +// ====================================================================== +// \title SeqDispatcher.hpp +// \author zimri.leisher +// \brief hpp file for SeqDispatcher component implementation class +// ====================================================================== + +#ifndef SeqDispatcher_HPP +#define SeqDispatcher_HPP + +#include "Svc/SeqDispatcher/SeqDispatcherComponentAc.hpp" +#include "Svc/SeqDispatcher/SeqDispatcher_CmdSequencerStateEnumAc.hpp" +#include "FppConstantsAc.hpp" +#include "Fw/Types/WaitEnumAc.hpp" +#include "Fw/Types/StringBase.hpp" + +namespace Svc { + +class SeqDispatcher : public SeqDispatcherComponentBase { + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object SeqDispatcher + //! + SeqDispatcher(const char* const compName /*!< The component name*/ + ); + + //! Destroy object SeqDispatcher + //! + ~SeqDispatcher(); + + PROTECTED: + + //! Handler for input port seqDoneIn + void + seqDoneIn_handler(NATIVE_INT_TYPE portNum, //!< The port number + FwOpcodeType opCode, //!< Command Op Code + U32 cmdSeq, //!< Command Sequence + const Fw::CmdResponse& response //!< The command response argument + ); + + //! Handler for input port seqStartIn + void seqStartIn_handler(NATIVE_INT_TYPE portNum, //!< The port number + const Fw::StringBase& fileName //!< The sequence file + ); + + //! Handler for input port seqRunIn + void seqRunIn_handler(NATIVE_INT_TYPE portNum, //!< The port number + const Fw::StringBase& fileName //!< The sequence file + ); + + PRIVATE: + + // number of sequences dispatched (successful or otherwise) + U32 m_dispatchedCount = 0; + // number of errors from dispatched sequences (CmdResponse::EXECUTION_ERROR) + U32 m_errorCount = 0; + // number of sequencers in state AVAILABLE + U32 m_sequencersAvailable = SeqDispatcherSequencerPorts; + + struct DispatchEntry { + FwOpcodeType opCode; //!< opcode of entry + U32 cmdSeq; + // store the state of each sequencer + SeqDispatcher_CmdSequencerState state; + // store the sequence currently running for each sequencer + Fw::String sequenceRunning = ""; + } m_entryTable[SeqDispatcherSequencerPorts]; //!< table of dispatch + //!< entries + + FwIndexType getNextAvailableSequencerIdx(); + + void runSequence(FwIndexType sequencerIdx, + const Fw::StringBase& fileName, + Fw::Wait block); + + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + //! Implementation for RUN command handler + //! + void RUN_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq, /*!< The command sequence number*/ + const Fw::CmdStringArg& fileName, /*!< The name of the sequence file*/ + Fw::Wait block); + + void LOG_STATUS_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq); /*!< The command sequence number*/ +}; + +} // end namespace components + +#endif diff --git a/Svc/SeqDispatcher/SeqDispatcherCommands.fppi b/Svc/SeqDispatcher/SeqDispatcherCommands.fppi new file mode 100644 index 0000000000..378c2f6ec7 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherCommands.fppi @@ -0,0 +1,9 @@ +@ Dispatches a sequence to the first available sequencer +async command RUN( + fileName: string size 240 @< The name of the sequence file + $block: Fw.Wait @< Return command status when complete or not + ) \ + opcode 0 + +@ Logs via Events the state of each connected command sequencer +async command LOG_STATUS() opcode 1 \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcherEvents.fppi b/Svc/SeqDispatcher/SeqDispatcherEvents.fppi new file mode 100644 index 0000000000..ec76d60c96 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherEvents.fppi @@ -0,0 +1,38 @@ +event InvalidSequencer( + idx: U16 +) \ + severity warning high \ + format "Invalid sequence index {}" + +event NoAvailableSequencers() \ + severity warning high \ + format "No available cmd sequencers to dispatch a sequence to" + +event UnknownSequenceFinished( + idx: U16 +) \ + severity warning low \ + format "Sequencer {} completed a sequence with no matching start notification" + +event ConflictingSequenceStarted( + idx: U16, + newSequence: string size 240, + sequenceInInternalState: string size 240 +) \ + severity warning high \ + format "Sequencer {} started a sequence {} while still running {}" + +event UnexpectedSequenceStarted( + idx: U16, + newSequence: string size 240 +) \ + severity warning low \ + format "Sequencer {} was externally commanded to start a sequence {}" + +event LogSequencerStatus( + idx: U16 + $state: CmdSequencerState + filename: string size 240 +) \ + severity activity low \ + format "Sequencer {} with state {} is running file {}" \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi b/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi new file mode 100644 index 0000000000..3728c9a063 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi @@ -0,0 +1,8 @@ +@ Number of sequences dispatched +telemetry dispatchedCount: U32 +@ Number of sequences dispatched that returned an error. Note: if a sequence +@ was run in non-blocking mode, even if the sequence errors out, this error +@ count will never increase +telemetry errorCount: U32 +@ Number of sequencers in an available state +telemetry sequencersAvailable: U32 \ No newline at end of file diff --git a/Svc/SeqDispatcher/docs/sdd.md b/Svc/SeqDispatcher/docs/sdd.md new file mode 100644 index 0000000000..86fa11160f --- /dev/null +++ b/Svc/SeqDispatcher/docs/sdd.md @@ -0,0 +1,51 @@ +# components::SeqDispatcher + +Dispatches command sequences to available command sequencers, allowing the spacecraft controllers to run multiple sequences at once without having to manually manage which `CmdSequencer`s those sequences run on. + +### Usage +* Call the `RUN` command just like you would call it on a `CmdSequencer` +* If any connected `CmdSequencer` is available, it will route the sequence to the first one it finds +* `RUN` can be made blocking or non-blocking, just like `CmdSequencer`'s `RUN` + +## State diagram +![State diagram of the SeqDispatcher](seq_dispatcher_model.png "SeqDispatcher model") + +## Port Descriptions +|Type| Name | Description | +|async input|seqRunIn|Equivalent to the RUN cmd, dispatches a sequence to the first available sequencer| +|output|seqRunOut|This is used by the SeqDispatcher to send sequence run calls to sequencers| +|async input|seqDoneIn|Called by a command sequencer whenever it has finished any sequence| +|async input|seqStartIn|Called by a command sequencer whenever it starts any sequence| + +## Commands +| Name | Description | +|RUN|Dispatches a sequence to the first available sequencer| +|LOG_STATUS|Logs via Events the state of each connected command sequencer| + +## Events +| Name | Description | +|InvalidSequencer|The given sequencer index is invalid for an unspecified reason| +|NoAvailableSequencers|There are no available sequencers to dispatch a sequence to| +|UnknownSequenceFinished|We received a call to seqDoneIn that didn't have a corresponding seqStartIn call| +|UnexpectedSequenceStarted|We received a call to seqStartIn but we didn't receive a call to seqDoneIn before that| +|LogSequencerStatus|Shows the current state and sequence filename for a particular sequencer. Produced by the LOG_STATUS command| + + + +## Telemetry +| Name | Description | +|dispatchedCount|Number of sequences dispatched| +|errorCount|Number of sequences dispatched that returned an error. Note: if a sequence was run in non-blocking mode, even if the sequence errors out, this error count will never increase| +|sequencersAvailable|Number of sequencers ready to run a sequence| + +## Unit Tests +Add unit test descriptions in the chart below +| Name | Description | +|testDispatch|Tests the basic dispatch functionality of the `SeqDispatcher`| +|testLogStatus|Tests the LOG_STATUS command| + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +|---|---|---| \ No newline at end of file diff --git a/Svc/SeqDispatcher/docs/seq_dispatcher_model.png b/Svc/SeqDispatcher/docs/seq_dispatcher_model.png new file mode 100644 index 0000000000..a85d7c7f45 Binary files /dev/null and b/Svc/SeqDispatcher/docs/seq_dispatcher_model.png differ diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp new file mode 100644 index 0000000000..b4362ef055 --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp @@ -0,0 +1,21 @@ +// ---------------------------------------------------------------------- +// TestMain.cpp +// ---------------------------------------------------------------------- + +#include "SeqDispatcherTester.hpp" + +TEST(Nominal, testDispatch) { + Svc::SeqDispatcherTester tester; + tester.testDispatch(); +} + +TEST(Nominal, testLogStatus) { + Svc::SeqDispatcherTester tester; + tester.testLogStatus(); +} + + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp new file mode 100644 index 0000000000..261f5a275c --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp @@ -0,0 +1,92 @@ +// ====================================================================== +// \title SeqDispatcher.hpp +// \author zimri.leisher +// \brief cpp file for SeqDispatcher test harness implementation class +// ====================================================================== + +#include "SeqDispatcherTester.hpp" + +namespace Svc{ + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +SeqDispatcherTester ::SeqDispatcherTester() + : SeqDispatcherGTestBase("SeqDispatcherTester", SeqDispatcherTester::MAX_HISTORY_SIZE), + component("SeqDispatcher") { + this->connectPorts(); + this->initComponents(); +} + +SeqDispatcherTester ::~SeqDispatcherTester() {} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- + +void SeqDispatcherTester ::testDispatch() { + // test that it fails when we dispatch too many sequences + for (int i = 0; i < SeqDispatcherSequencerPorts; i++) { + sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::WAIT); + this->component.doDispatch(); + // no response cuz blocking + ASSERT_CMD_RESPONSE_SIZE(0); + ASSERT_EVENTS_SIZE(0); + } + ASSERT_TLM_sequencersAvailable(SeqDispatcherSequencerPorts - 1, 0); + this->clearHistory(); + // all sequencers should be busy + sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::WAIT); + this->component.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, + Fw::CmdResponse::EXECUTION_ERROR); + + this->clearHistory(); + + this->invoke_to_seqDoneIn(0, 0, 0, Fw::CmdResponse::OK); + this->component.doDispatch(); + ASSERT_EVENTS_SIZE(0); + // we should have gotten a cmd response now + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::OK); + + this->clearHistory(); + // ok now we should be able to send another sequence + // let's test non blocking now + sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::NO_WAIT); + this->component.doDispatch(); + + // should immediately return + ASSERT_EVENTS_SIZE(0); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::OK); + this->clearHistory(); + + // ok now check that if a sequence errors on block it will return error + this->invoke_to_seqDoneIn(1, 0, 0, Fw::CmdResponse::EXECUTION_ERROR); + this->component.doDispatch(); + ASSERT_EVENTS_SIZE(0); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::EXECUTION_ERROR); + +} + +void SeqDispatcherTester::testLogStatus() { + this->sendCmd_RUN(0,0, Fw::String("test"), Fw::Wait::WAIT); + this->component.doDispatch(); + this->sendCmd_LOG_STATUS(0,0); + this->component.doDispatch(); + ASSERT_EVENTS_SIZE(SeqDispatcherSequencerPorts); + ASSERT_EVENTS_LogSequencerStatus(0, 0, SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK, "test"); +} + +void SeqDispatcherTester::seqRunOut_handler( + FwIndexType portNum, //!< The port number + const Fw::StringBase& filename //!< The sequence file +) { + this->pushFromPortEntry_seqRunOut(filename); +} + +} // end namespace components diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp new file mode 100644 index 0000000000..f1f9aee162 --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp @@ -0,0 +1,79 @@ +// ====================================================================== +// \title SeqDispatcher/test/ut/Tester.hpp +// \author zimri.leisher +// \brief hpp file for SeqDispatcher test harness implementation class +// ====================================================================== + +#ifndef TESTER_HPP +#define TESTER_HPP + +#include "SeqDispatcherGTestBase.hpp" +#include "Svc/SeqDispatcher/SeqDispatcher.hpp" + +namespace Svc{ + +class SeqDispatcherTester : public SeqDispatcherGTestBase { + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + public: + // Maximum size of histories storing events, telemetry, and port outputs + static const NATIVE_INT_TYPE MAX_HISTORY_SIZE = 10; + // Instance ID supplied to the component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_ID = 0; + // Queue depth supplied to component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_QUEUE_DEPTH = 10; + + //! Construct object SeqDispatcherTester + //! + SeqDispatcherTester(); + + //! Destroy object SeqDispatcherTester + //! + ~SeqDispatcherTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + void testDispatch(); + void testLogStatus(); + + private: + // ---------------------------------------------------------------------- + // Handlers for typed from ports + // ---------------------------------------------------------------------- + + void seqRunOut_handler( + FwIndexType portNum, //!< The port number + const Fw::StringBase& filename //!< The sequence file + ); + + private: + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + //! Connect ports + //! + void connectPorts(); + + //! Initialize components + //! + void initComponents(); + + private: + // ---------------------------------------------------------------------- + // Variables + // ---------------------------------------------------------------------- + + //! The component under test + //! + SeqDispatcher component; +}; + +} // end namespace components + +#endif diff --git a/Svc/StaticMemory/StaticMemoryComponentImpl.cpp b/Svc/StaticMemory/StaticMemoryComponentImpl.cpp index 978152f6cf..cec600346c 100644 --- a/Svc/StaticMemory/StaticMemoryComponentImpl.cpp +++ b/Svc/StaticMemory/StaticMemoryComponentImpl.cpp @@ -27,10 +27,6 @@ StaticMemoryComponentImpl ::StaticMemoryComponentImpl(const char* const compName } } -void StaticMemoryComponentImpl ::init(const NATIVE_INT_TYPE instance) { - StaticMemoryComponentBase::init(instance); -} - StaticMemoryComponentImpl ::~StaticMemoryComponentImpl() {} // ---------------------------------------------------------------------- diff --git a/Svc/StaticMemory/StaticMemoryComponentImpl.hpp b/Svc/StaticMemory/StaticMemoryComponentImpl.hpp index 97d0e8e3e4..5eeb14d16e 100644 --- a/Svc/StaticMemory/StaticMemoryComponentImpl.hpp +++ b/Svc/StaticMemory/StaticMemoryComponentImpl.hpp @@ -29,11 +29,6 @@ class StaticMemoryComponentImpl : public StaticMemoryComponentBase { StaticMemoryComponentImpl(const char* const compName /*!< The component name*/ ); - //! Initialize object StaticMemory - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object StaticMemory //! ~StaticMemoryComponentImpl(); diff --git a/Svc/SystemResources/CMakeLists.txt b/Svc/SystemResources/CMakeLists.txt index 6fb9cfa7ea..c4a34de837 100644 --- a/Svc/SystemResources/CMakeLists.txt +++ b/Svc/SystemResources/CMakeLists.txt @@ -12,7 +12,6 @@ set(SOURCE_FILES ) set(MOD_DEPS Os - version ) register_fprime_module() ### UTs ### diff --git a/Svc/SystemResources/SystemResources.cpp b/Svc/SystemResources/SystemResources.cpp index 1c2bd04299..b833792074 100644 --- a/Svc/SystemResources/SystemResources.cpp +++ b/Svc/SystemResources/SystemResources.cpp @@ -12,7 +12,6 @@ #include //isnan() #include -#include #include namespace Svc { @@ -58,10 +57,6 @@ SystemResources ::SystemResources(const char* const compName) m_cpu_tlm_functions[15] = &Svc::SystemResources::tlmWrite_CPU_15; } -void SystemResources ::init(const NATIVE_INT_TYPE instance) { - SystemResourcesComponentBase::init(instance); -} - SystemResources ::~SystemResources() {} // ---------------------------------------------------------------------- @@ -73,7 +68,6 @@ void SystemResources ::run_handler(const NATIVE_INT_TYPE portNum, U32 tick_time_ Cpu(); Mem(); PhysMem(); - Version(); } } @@ -88,15 +82,6 @@ void SystemResources ::ENABLE_cmdHandler(const FwOpcodeType opCode, this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } -void SystemResources ::VERSION_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) { - Fw::LogStringArg version_string(Project::Version::FRAMEWORK_VERSION); - this->log_ACTIVITY_LO_FRAMEWORK_VERSION(version_string); - - version_string = Project::Version::PROJECT_VERSION; - this->log_ACTIVITY_LO_PROJECT_VERSION(version_string); - this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); -} - F32 SystemResources::compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous) { F32 util = 100.0f; // Prevent divide by zero on fast-sample @@ -151,11 +136,4 @@ void SystemResources::PhysMem() { } } -void SystemResources::Version() { - Fw::TlmString version_string(Project::Version::FRAMEWORK_VERSION); - this->tlmWrite_FRAMEWORK_VERSION(version_string); - - version_string= Project::Version::PROJECT_VERSION; - this->tlmWrite_PROJECT_VERSION(version_string); -} } // end namespace Svc diff --git a/Svc/SystemResources/SystemResources.fpp b/Svc/SystemResources/SystemResources.fpp index fa7d271dd7..1c2d0f4ad5 100644 --- a/Svc/SystemResources/SystemResources.fpp +++ b/Svc/SystemResources/SystemResources.fpp @@ -42,26 +42,6 @@ module Svc { ) \ opcode 0 - @ Report version as EVR - guarded command VERSION \ - opcode 1 - - @ Version of the git repository. - event FRAMEWORK_VERSION( - version: string size 40 @< version string - ) \ - severity activity low \ - id 0 \ - format "Framework Version: [{}]" - - @ Version of the git repository. - event PROJECT_VERSION( - version: string size 40 @< version string - ) \ - severity activity low \ - id 1 \ - format "Project Version: [{}]" - @ Total system memory in KB telemetry MEMORY_TOTAL: U64 id 0 \ format "{} KB" @@ -129,12 +109,6 @@ module Svc { @ System's CPU Percentage telemetry CPU_15: F32 id 20 format "{.2f} percent" - @ Software framework version - telemetry FRAMEWORK_VERSION: string size 40 id 21 - - @ Software project version - telemetry PROJECT_VERSION: string size 40 id 22 - } } diff --git a/Svc/SystemResources/SystemResources.hpp b/Svc/SystemResources/SystemResources.hpp index 7807a7e067..44485943db 100644 --- a/Svc/SystemResources/SystemResources.hpp +++ b/Svc/SystemResources/SystemResources.hpp @@ -30,11 +30,6 @@ class SystemResources : public SystemResourcesComponentBase { SystemResources(const char* const compName /*!< The component name*/ ); - //! Initialize object SystemResources - //! - void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object SystemResources //! ~SystemResources(void); @@ -67,17 +62,11 @@ class SystemResources : public SystemResourcesComponentBase { SystemResourceEnabled enable /*!< whether or not system resource telemetry is enabled*/ ); - //! Implementation for VERSION command handler - //! Report version as EVR - void VERSION_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ - const U32 cmdSeq /*!< The command sequence number*/ - ); private: void Cpu(); void Mem(); void PhysMem(); - void Version(); F32 compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous); diff --git a/Svc/SystemResources/docs/sdd.md b/Svc/SystemResources/docs/sdd.md index db0160c203..37b2757a1b 100644 --- a/Svc/SystemResources/docs/sdd.md +++ b/Svc/SystemResources/docs/sdd.md @@ -3,10 +3,9 @@ The system resources component downlinks information about the running F´ system. This information includes: -1. System and Software version -2. Free Memory -3. CPU load -4. Disk space +1. Free Memory +2. CPU load +3. Disk space These items are downlinked as telemetry channels in response to a rate group port invocation. diff --git a/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp b/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp index 36c992659d..37558da3f1 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp @@ -14,11 +14,6 @@ TEST(OffNominal, Disabled) { tester.test_disable_enable(); } -TEST(Nominal, Events) { - Svc::SystemResourcesTester tester; - tester.test_version_evr(); -} - int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/Svc/SystemResources/test/ut/SystemResourcesTester.cpp b/Svc/SystemResources/test/ut/SystemResourcesTester.cpp index 476a66d6a6..fe9b1adb12 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTester.cpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTester.cpp @@ -11,7 +11,6 @@ // ====================================================================== #include "SystemResourcesTester.hpp" -#include "versions/version.hpp" #define INSTANCE 0 #define MAX_HISTORY_SIZE 100 @@ -111,13 +110,7 @@ void SystemResourcesTester ::test_tlm(bool enabled) { ASSERT_TLM_NON_VOLATILE_FREE_SIZE(0); ASSERT_TLM_NON_VOLATILE_TOTAL_SIZE(0); } - ASSERT_TLM_FRAMEWORK_VERSION_SIZE((enabled) ? 1 : 0); - ASSERT_TLM_PROJECT_VERSION_SIZE((enabled) ? 1 : 0); - if (enabled) { - ASSERT_TLM_FRAMEWORK_VERSION(0, Project::Version::FRAMEWORK_VERSION); - ASSERT_TLM_PROJECT_VERSION(0, Project::Version::PROJECT_VERSION); - } - ASSERT_TLM_SIZE((enabled) ? (count + 3) : 0); // CPU count channels + avg + 2 ver + ASSERT_TLM_SIZE((enabled) ? (count + 1) : 0); // CPU count channels + avg + 2 ver break; } } @@ -130,14 +123,6 @@ void SystemResourcesTester ::test_disable_enable() { this->test_tlm(true); } -void SystemResourcesTester ::test_version_evr() { - this->sendCmd_VERSION(0, 0); - ASSERT_EVENTS_FRAMEWORK_VERSION_SIZE(1); - ASSERT_EVENTS_FRAMEWORK_VERSION(0, Project::Version::FRAMEWORK_VERSION); - ASSERT_EVENTS_PROJECT_VERSION_SIZE(1); - ASSERT_EVENTS_PROJECT_VERSION(0, Project::Version::FRAMEWORK_VERSION); -} - // ---------------------------------------------------------------------- // Helper methods // ---------------------------------------------------------------------- diff --git a/Svc/SystemResources/test/ut/SystemResourcesTester.hpp b/Svc/SystemResources/test/ut/SystemResourcesTester.hpp index 6c3fcff571..392d70eddd 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTester.hpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTester.hpp @@ -45,10 +45,6 @@ class SystemResourcesTester : public SystemResourcesGTestBase { //! void test_disable_enable(); - //! Test version EVR - //! - void test_version_evr(); - private: // ---------------------------------------------------------------------- // Helper methods diff --git a/Svc/TlmChan/TlmChan.cpp b/Svc/TlmChan/TlmChan.cpp index 924dd59ba0..274db774a1 100644 --- a/Svc/TlmChan/TlmChan.cpp +++ b/Svc/TlmChan/TlmChan.cpp @@ -42,12 +42,6 @@ TlmChan::TlmChan(const char* name) : TlmChanComponentBase(name), m_activeBuffer( TlmChan::~TlmChan() {} -void TlmChan::init(NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ -) { - TlmChanComponentBase::init(queueDepth, instance); -} - NATIVE_UINT_TYPE TlmChan::doHash(FwChanIdType id) { return (id % TLMCHAN_HASH_MOD_VALUE) % TLMCHAN_NUM_TLM_HASH_SLOTS; } diff --git a/Svc/TlmChan/TlmChan.hpp b/Svc/TlmChan/TlmChan.hpp index 0aca5b13a6..81be500fff 100644 --- a/Svc/TlmChan/TlmChan.hpp +++ b/Svc/TlmChan/TlmChan.hpp @@ -23,9 +23,6 @@ class TlmChan : public TlmChanComponentBase { public: TlmChan(const char* compName); virtual ~TlmChan(); - void init(NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - NATIVE_INT_TYPE instance /*!< The instance number*/ - ); PROTECTED: // can be overridden for alternate algorithms diff --git a/Svc/TlmPacketizer/TlmPacketizer.cpp b/Svc/TlmPacketizer/TlmPacketizer.cpp index 4c2d693828..34cdcaa1eb 100644 --- a/Svc/TlmPacketizer/TlmPacketizer.cpp +++ b/Svc/TlmPacketizer/TlmPacketizer.cpp @@ -47,10 +47,6 @@ TlmPacketizer ::TlmPacketizer(const char* const compName) } } -void TlmPacketizer ::init(const NATIVE_INT_TYPE queueDepth, const NATIVE_INT_TYPE instance) { - TlmPacketizerComponentBase::init(queueDepth, instance); -} - TlmPacketizer ::~TlmPacketizer() {} void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList, diff --git a/Svc/TlmPacketizer/TlmPacketizer.hpp b/Svc/TlmPacketizer/TlmPacketizer.hpp index 30e765b0b1..b2a8af1ce2 100644 --- a/Svc/TlmPacketizer/TlmPacketizer.hpp +++ b/Svc/TlmPacketizer/TlmPacketizer.hpp @@ -29,12 +29,6 @@ class TlmPacketizer : public TlmPacketizerComponentBase { TlmPacketizer(const char* const compName /*!< The component name*/ ); - //! Initialize object TlmPacketizer - //! - void init(const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - void setPacketList( const TlmPacketizerPacketList& packetList, // channels to packetize const Svc::TlmPacketizerPacket& ignoreList, // channels to ignore (i.e. no warning event if not packetized) diff --git a/Svc/UdpReceiver/UdpReceiverComponentImpl.cpp b/Svc/UdpReceiver/UdpReceiverComponentImpl.cpp index 71042dc162..0a94b2cd1e 100644 --- a/Svc/UdpReceiver/UdpReceiverComponentImpl.cpp +++ b/Svc/UdpReceiver/UdpReceiverComponentImpl.cpp @@ -46,14 +46,6 @@ namespace Svc { } - void UdpReceiverComponentImpl :: - init( - const NATIVE_INT_TYPE instance - ) - { - UdpReceiverComponentBase::init(instance); - } - UdpReceiverComponentImpl :: ~UdpReceiverComponentImpl() { diff --git a/Svc/UdpReceiver/UdpReceiverComponentImpl.hpp b/Svc/UdpReceiver/UdpReceiverComponentImpl.hpp index 2d2da13bf7..4e61faea9e 100644 --- a/Svc/UdpReceiver/UdpReceiverComponentImpl.hpp +++ b/Svc/UdpReceiver/UdpReceiverComponentImpl.hpp @@ -34,12 +34,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize object UdpReceiver - //! - void init( - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - //! Destroy object UdpReceiver //! ~UdpReceiverComponentImpl(); diff --git a/Svc/UdpSender/UdpSenderComponentImpl.cpp b/Svc/UdpSender/UdpSenderComponentImpl.cpp index e409b3d162..ad5e76b16d 100644 --- a/Svc/UdpSender/UdpSenderComponentImpl.cpp +++ b/Svc/UdpSender/UdpSenderComponentImpl.cpp @@ -40,16 +40,6 @@ namespace Svc { } - void UdpSenderComponentImpl :: - init( - const NATIVE_INT_TYPE queueDepth, - const NATIVE_INT_TYPE msgSize, - const NATIVE_INT_TYPE instance - ) - { - UdpSenderComponentBase::init(queueDepth, msgSize, instance); - } - UdpSenderComponentImpl :: ~UdpSenderComponentImpl() { diff --git a/Svc/UdpSender/UdpSenderComponentImpl.hpp b/Svc/UdpSender/UdpSenderComponentImpl.hpp index fa136f681e..6c3a75c704 100644 --- a/Svc/UdpSender/UdpSenderComponentImpl.hpp +++ b/Svc/UdpSender/UdpSenderComponentImpl.hpp @@ -37,15 +37,6 @@ namespace Svc { const char *const compName /*!< The component name*/ ); - //! Initialize object UdpSender - //! - void init( - const NATIVE_INT_TYPE queueDepth, /*!< The queue depth*/ - const NATIVE_INT_TYPE msgSize, /*!< The message size*/ - const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ - ); - - //! Destroy object UdpSender //! ~UdpSenderComponentImpl(); diff --git a/Svc/Version/Version.cpp b/Svc/Version/Version.cpp index c659ce688d..70f94f449d 100644 --- a/Svc/Version/Version.cpp +++ b/Svc/Version/Version.cpp @@ -112,7 +112,7 @@ void Version ::VERSION_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Svc::VersionT // ---------------------------------------------------------------------- // implementations for internal functions // ---------------------------------------------------------------------- -// functions to log tlm on versions +// Send both events and tlm for framework version void Version ::fwVersion_tlm() { Fw::LogStringArg fw_event = (Project::Version::FRAMEWORK_VERSION); this->log_ACTIVITY_LO_FrameworkVersion(fw_event); @@ -120,6 +120,7 @@ void Version ::fwVersion_tlm() { this->tlmWrite_FrameworkVersion(fw_tlm); } +// Send both events and tlm for project version void Version ::projectVersion_tlm() { Fw::LogStringArg proj_event = Project::Version::PROJECT_VERSION; this->log_ACTIVITY_LO_ProjectVersion(proj_event); @@ -127,6 +128,7 @@ void Version ::projectVersion_tlm() { this->tlmWrite_ProjectVersion(proj_tlm); } +// Send both events and tlm for library versions void Version ::libraryVersion_tlm() { // Process libraries array @@ -173,6 +175,7 @@ void Version ::libraryVersion_tlm() { } } +// Send all events and tlm (if verbosity is enabled) for custom versions void Version ::customVersion_tlm_all() { for (U8 i = 0; (m_enable == true) && (m_num_custom_elements != 0) && (i < Svc::VersionCfg::VersionEnum::NUM_CONSTANTS); i++) { @@ -180,52 +183,55 @@ void Version ::customVersion_tlm_all() { } } +// Send events and tlm (if verbosity is enabled) for custom versions void Version ::customVersion_tlm(VersionSlot custom_slot) { // Process custom version TLM only if verbosity is enabled and there are any valid writes to it; // it doesn't necessarily have to be consecutive - if ((this->verId_db[custom_slot].getversion_value() != "no_ver") && m_enable == true && + if ((this->verId_db[custom_slot].getversion_value() != "no_ver") && (this->m_num_custom_elements > 0)) { // Write TLM for valid writes - // Emit Events/TLM on library versions + // Emit Events/TLM on custom versions this->log_ACTIVITY_LO_CustomVersions(this->verId_db[custom_slot].getversion_enum(), this->verId_db[custom_slot].getversion_value()); + if (m_enable == true) { //Send TLM only if verbosity is enabled // Write to TLM - switch (custom_slot) { - case VER_SLOT_00: - this->tlmWrite_CustomVersion01(verId_db[custom_slot]); - break; - case VER_SLOT_01: - this->tlmWrite_CustomVersion02(verId_db[custom_slot]); - break; - case VER_SLOT_02: - this->tlmWrite_CustomVersion03(verId_db[custom_slot]); - break; - case VER_SLOT_03: - this->tlmWrite_CustomVersion04(verId_db[custom_slot]); - break; - case VER_SLOT_04: - this->tlmWrite_CustomVersion05(verId_db[custom_slot]); - break; - case VER_SLOT_05: - this->tlmWrite_CustomVersion06(verId_db[custom_slot]); - break; - case VER_SLOT_06: - this->tlmWrite_CustomVersion07(verId_db[custom_slot]); - break; - case VER_SLOT_07: - this->tlmWrite_CustomVersion08(verId_db[custom_slot]); - break; - case VER_SLOT_08: - this->tlmWrite_CustomVersion09(verId_db[custom_slot]); - break; - case VER_SLOT_09: - this->tlmWrite_CustomVersion10(verId_db[custom_slot]); - break; - default: - // There are only 10 custom slots available - FW_ASSERT(0, custom_slot); - break; + switch (custom_slot) { + case VER_SLOT_00: + this->tlmWrite_CustomVersion01(verId_db[custom_slot]); + break; + case VER_SLOT_01: + this->tlmWrite_CustomVersion02(verId_db[custom_slot]); + break; + case VER_SLOT_02: + this->tlmWrite_CustomVersion03(verId_db[custom_slot]); + break; + case VER_SLOT_03: + this->tlmWrite_CustomVersion04(verId_db[custom_slot]); + break; + case VER_SLOT_04: + this->tlmWrite_CustomVersion05(verId_db[custom_slot]); + break; + case VER_SLOT_05: + this->tlmWrite_CustomVersion06(verId_db[custom_slot]); + break; + case VER_SLOT_06: + this->tlmWrite_CustomVersion07(verId_db[custom_slot]); + break; + case VER_SLOT_07: + this->tlmWrite_CustomVersion08(verId_db[custom_slot]); + break; + case VER_SLOT_08: + this->tlmWrite_CustomVersion09(verId_db[custom_slot]); + break; + case VER_SLOT_09: + this->tlmWrite_CustomVersion10(verId_db[custom_slot]); + break; + default: + // There are only 10 custom slots available + FW_ASSERT(0, custom_slot); + break; + } } } } diff --git a/Svc/Version/docs/sdd.md b/Svc/Version/docs/sdd.md index 6fe1b50bd1..1388c2bd07 100644 --- a/Svc/Version/docs/sdd.md +++ b/Svc/Version/docs/sdd.md @@ -9,18 +9,18 @@ Tracks versions for framework,project, libraries and user defined project specif |SVC-VERSION-001|`Svc::Version` upon startup shall generate an event and a telemetry channel with version for framework.| This is to provide transparency on framework version being used| |SVC-VERSION-002|`Svc::Version` upon startup shall generate an event and a telemetry channel with version for project | This is to provide transparency on project version being used| |SVC-VERSION-003|`Svc::Version` upon startup shall generate events and telemetry channels (upto 10) with versions for library.| Transparency on different library versions| -|SVC-VERSION-004|`Svc::Version` upon startup shall make verbosity on custom versions configurable.| Transparency on different library versions| +|SVC-VERSION-004|`Svc::Version` upon startup shall make verbosity on custom versions configurable.| Verbosity will enable/disable the channel generation but will not effect the event generation| |SVC-VERSION-005|`Svc::Version` shall provide a ground command to request events and telemetry on framework version| Accessibility on demand| |SVC-VERSION-006|`Svc::Version` shall provide a ground command to request events and telemetry on project version| Accessibility on demand| |SVC-VERSION-007|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on library versions| Accessibility on demand| -|SVC-VERSION-008|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on custom versions| Accessibility on demand| +|SVC-VERSION-008|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on custom versions| Accessibility on demand. Verbosity configuration will determine the channel generation| |SVC-VERSION-009|`Svc::Version` shall provide a ground command to enable/disable verbosity on custom versions| Accessibility on demand| |SVC-VERSION-010|`Svc::Version` shall provide a telemetry channel on framework version| Accessibility to versions being used| |SVC-VERSION-011|`Svc::Version` shall provide a telemetry channel on project version| Accessibility to versions being used| |SVC-VERSION-012|`Svc::Version` shall provide upto 10 telemetry channels on library versions| Accessibility to versions being used| -|SVC-VERSION-013|`Svc::Version` shall provide upto 10 telemetry channels on custom versions| Accessibility to versions being used| -|SVC-VERSION-014|`Svc::Version` shall provide an interface for other components to set custom versions.| Enables projects to set hardware and FPGA versions, say, as needed. Also generates Events/TLM| -|SVC-VERSION-015|`Svc::Version` shall provide an interface for other components to get custom versions.| Also generates Events/TLM| +|SVC-VERSION-013|`Svc::Version` shall provide upto 10 telemetry channels on custom versions| Accessibility to versions being used. Only accessible if verbosity is enabled| +|SVC-VERSION-014|`Svc::Version` shall provide an interface for other components to set custom versions.| Enables projects to set hardware and FPGA versions, say, as needed. Also generates Events/TLM based on verbosity configuration| +|SVC-VERSION-015|`Svc::Version` shall provide an interface for other components to get custom versions.| Also generates Events/TLM based on verbosity configuration| ## Emitting Versions on Start-Up diff --git a/Svc/Version/test/ut/VersionTester.cpp b/Svc/Version/test/ut/VersionTester.cpp index 7fa4f40f01..edc114128d 100644 --- a/Svc/Version/test/ut/VersionTester.cpp +++ b/Svc/Version/test/ut/VersionTester.cpp @@ -85,13 +85,35 @@ void VersionTester ::test_enable() { this->sendCmd_ENABLE(0, cmd_seq, VersionEnabled::DISABLED); ASSERT_CMD_RESPONSE(0, 0, 9, Fw::CmdResponse::OK); VersionTester::test_setVer(false); - ASSERT_EVENTS_CustomVersions_SIZE(0); + //When verbosity is disabled, events are still generated but not EHAs + ASSERT_EVENTS_CustomVersions_SIZE(10); + ASSERT_TLM_CustomVersion01_SIZE(0); + ASSERT_TLM_CustomVersion02_SIZE(0); + ASSERT_TLM_CustomVersion03_SIZE(0); + ASSERT_TLM_CustomVersion04_SIZE(0); + ASSERT_TLM_CustomVersion05_SIZE(0); + ASSERT_TLM_CustomVersion06_SIZE(0); + ASSERT_TLM_CustomVersion07_SIZE(0); + ASSERT_TLM_CustomVersion08_SIZE(0); + ASSERT_TLM_CustomVersion09_SIZE(0); + ASSERT_TLM_CustomVersion10_SIZE(0); cmd_seq = 9; this->sendCmd_ENABLE(0, cmd_seq, VersionEnabled::ENABLED); ASSERT_CMD_RESPONSE(0, 0, 9, Fw::CmdResponse::OK); VersionTester::test_setVer(true); + //When verbosity is enabled, events and EHAs are generated ASSERT_EVENTS_CustomVersions_SIZE(10); + ASSERT_TLM_CustomVersion01_SIZE(1); + ASSERT_TLM_CustomVersion02_SIZE(1); + ASSERT_TLM_CustomVersion03_SIZE(1); + ASSERT_TLM_CustomVersion04_SIZE(1); + ASSERT_TLM_CustomVersion05_SIZE(1); + ASSERT_TLM_CustomVersion06_SIZE(1); + ASSERT_TLM_CustomVersion07_SIZE(1); + ASSERT_TLM_CustomVersion08_SIZE(1); + ASSERT_TLM_CustomVersion09_SIZE(1); + ASSERT_TLM_CustomVersion10_SIZE(1); } void VersionTester ::test_versions() { diff --git a/Utils/Types/CircularBuffer.cpp b/Utils/Types/CircularBuffer.cpp index 0eac02c513..cbb64d230d 100644 --- a/Utils/Types/CircularBuffer.cpp +++ b/Utils/Types/CircularBuffer.cpp @@ -17,7 +17,7 @@ #include #ifdef CIRCULAR_DEBUG - #include + #include #endif namespace Types { @@ -169,12 +169,12 @@ void CircularBuffer ::clear_high_water_mark() { #ifdef CIRCULAR_DEBUG void CircularBuffer :: print() { NATIVE_UINT_TYPE idx = m_head_idx; - Os::Log::log("Ring: "); + Fw::Logger::log("Ring: "); for (NATIVE_UINT_TYPE i = 0; i < m_allocated_size; ++i) { - Os::Log::log("%c", m_store[idx]); + Fw::Logger::log("%02x ", m_store[idx]); idx = advance_idx(idx); } - Os::Log::log("\n"); + Fw::Logger::log("\n"); } #endif } //End Namespace Types diff --git a/cmake/platform/Darwin.cmake b/cmake/platform/Darwin.cmake index efb41dd128..35a0049320 100644 --- a/cmake/platform/Darwin.cmake +++ b/cmake/platform/Darwin.cmake @@ -19,6 +19,8 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER) FIND_PACKAGE ( Threads REQUIRED ) endif() choose_fprime_implementation(Os/File Os/File/Posix) +choose_fprime_implementation(Os/FileSystem Os/FileSystem/Posix) +choose_fprime_implementation(Os/Directory Os/Directory/Posix) choose_fprime_implementation(Os/Console Os/Console/Posix) choose_fprime_implementation(Os/Task Os/Task/Posix) choose_fprime_implementation(Os/Mutex Os/Mutex/Posix) diff --git a/cmake/platform/Linux.cmake b/cmake/platform/Linux.cmake index 14bd5146db..9b3fc41508 100644 --- a/cmake/platform/Linux.cmake +++ b/cmake/platform/Linux.cmake @@ -10,6 +10,8 @@ if (NOT DEFINED FPRIME_USE_BAREMETAL_SCHEDULER) FIND_PACKAGE ( Threads REQUIRED ) endif() choose_fprime_implementation(Os/File Os/File/Posix) +choose_fprime_implementation(Os/FileSystem Os/FileSystem/Posix) +choose_fprime_implementation(Os/Directory Os/Directory/Posix) choose_fprime_implementation(Os/Console Os/Console/Posix) choose_fprime_implementation(Os/Task Os/Task/Posix) choose_fprime_implementation(Os/Mutex Os/Mutex/Posix) diff --git a/cmake/target/check.cmake b/cmake/target/check.cmake index 41e2800c28..56421936ee 100644 --- a/cmake/target/check.cmake +++ b/cmake/target/check.cmake @@ -62,10 +62,7 @@ function(check_add_module_target MODULE_NAME TARGET_NAME SOURCE_FILES DEPENDENCI if (NOT BUILD_TESTING OR NOT MODULE_TYPE STREQUAL "Unit Test") return() endif() - # UTs MODULE_NAME defaults to _ut_exe - # The below handling gives CHECK_TARGET_NAME = _check - string(REGEX REPLACE "_${UT_TARGET}$" "" CHECK_TARGET_NAME "${MODULE_NAME}") - string(APPEND CHECK_TARGET_NAME "_${TARGET_NAME}") + set(CHECK_TARGET_NAME "${FPRIME_CURRENT_MODULE}_${TARGET_NAME}") if (NOT TARGET ${CHECK_TARGET_NAME}) add_custom_target( "${CHECK_TARGET_NAME}" diff --git a/config/AcConstants.fpp b/config/AcConstants.fpp index 2f0f10706c..3ddcff26e6 100644 --- a/config/AcConstants.fpp +++ b/config/AcConstants.fpp @@ -18,6 +18,9 @@ constant CmdDispatcherComponentCommandPorts = 30 @ Used for uplink/sequencer buffer/response ports constant CmdDispatcherSequencePorts = 5 +@ Used for dispatching sequences to command sequencers +constant SeqDispatcherSequencerPorts = 2 + @ Used for sizing the command splitter input arrays constant CmdSplitterPorts = CmdDispatcherSequencePorts diff --git a/config/FpConfig.h b/config/FpConfig.h index 5e4bde9d65..c6e0e8db37 100644 --- a/config/FpConfig.h +++ b/config/FpConfig.h @@ -8,10 +8,11 @@ * ALL RIGHTS RESERVED. United States Government Sponsorship * acknowledged. */ -#include #ifndef FPCONFIG_H_ #define FPCONFIG_H_ +#include + // ---------------------------------------------------------------------- // Type aliases // ---------------------------------------------------------------------- @@ -276,6 +277,11 @@ typedef FwIndexType FwQueueSizeType; #define FW_COM_BUFFER_MAX_SIZE 512 #endif +// Specifies the size of the buffer attached to state machine signals. +#ifndef FW_SM_SIGNAL_BUFFER_MAX_SIZE +#define FW_SM_SIGNAL_BUFFER_MAX_SIZE 128 // Not to exceed size of NATIVE_UINT_TYPE +#endif + // Specifies the size of the buffer that contains the serialized command arguments. #ifndef FW_CMD_ARG_BUFFER_MAX_SIZE @@ -382,12 +388,21 @@ typedef FwIndexType FwQueueSizeType; #define FW_HANDLE_MAX_SIZE 72 //!< Maximum size of a handle for OS resources (files, queues, locks, etc.) #endif +#ifndef FW_DIRECTORY_HANDLE_MAX_SIZE +#define FW_DIRECTORY_HANDLE_MAX_SIZE 72 //!< Maximum size of a handle for OS resources (files, queues, locks, etc.) +#endif + +#ifndef FW_FILESYSTEM_HANDLE_MAX_SIZE +#define FW_FILESYSTEM_HANDLE_MAX_SIZE 72 //!< Maximum size of a handle for OS resources (files, queues, locks, etc.) +#endif + #ifndef FW_HANDLE_ALIGNMENT #define FW_HANDLE_ALIGNMENT 8 //!< Alignment of handle storage #endif +// Note: One buffer of this size will be stack-allocated during certain OSAL operations e.g. when copying a file #ifndef FW_FILE_CHUNK_SIZE -#define FW_FILE_CHUNK_SIZE 512 //!< Chunk size for working with files +#define FW_FILE_CHUNK_SIZE 512 //!< Chunk size for working with files in the OSAL layer #endif // *** NOTE configuration checks are in Fw/Cfg/ConfigCheck.cpp in order to have diff --git a/docs/Tutorials/CrossCompilationSetup/ArmLinuxTutorial.md b/docs/Tutorials/CrossCompilationSetup/ArmLinuxTutorial.md index e25a989346..ac4eddad2a 100644 --- a/docs/Tutorials/CrossCompilationSetup/ArmLinuxTutorial.md +++ b/docs/Tutorials/CrossCompilationSetup/ArmLinuxTutorial.md @@ -6,25 +6,25 @@ First, in a terminal upload the software to hardware platform. This is done with ```sh # For ARM 64-bit hardware -# In: Deployment Folder +# In: project root folder scp -r build-artifacts/aarch64-linux/ @:deployment # For ARM 32-bit hardware -# In: Deployment Folder +# In: project root folder scp -r build-artifacts/arm-hf-linux/ @:deployment ``` > Users must fill in the username and device address above. Next run the F´ GDS without launching the native compilation (`-n`) and with the -dictionary from the build above (`--dictionary ./build-artifacts///dict/<.xml document>`). +dictionary from the build above (`--dictionary ../build-artifacts///dict/<.xml document>`). ```sh # For in-person workshops and ARM 64-bit hardware -# In: Deployment Folder +# In: project root folder fprime-gds -n --dictionary build-artifacts/aarch64-linux//dict/.xml --ip-client --ip-address # For ARM 32-bit hardware -# In: Deployment Folder +# In: project root folder fprime-gds -n --dictionary build-artifacts/aarch64-linux//dict/.xml --ip-client --ip-address ``` > This depends on a flight software deployment that uses TcpServer as the communications driver implementation. diff --git a/docs/UsersGuide/dev/configuring-fprime.md b/docs/UsersGuide/dev/configuring-fprime.md index 6464fb8732..dc9ee6ea55 100644 --- a/docs/UsersGuide/dev/configuring-fprime.md +++ b/docs/UsersGuide/dev/configuring-fprime.md @@ -53,7 +53,7 @@ number of components. | CmdDispatcherSequencePorts | Number of incoming ports to command dispatcher, e.g. uplink and command sequencer | 5 | Positive integer | | RateGroupDriverRateGroupPorts | Number of rate group driver output ports. Limits total number of different rate groups | 3 | Positive integer | | HealthPingPorts | Number of health ping output ports. Limits number of components attached to health component | 25 | Positive integer | - +| SeqDispatcherSequencerPorts | Number of CmdSequencers that the SeqDispatcher can dispatch sequences to | 2 | Positive integer ## FpConfig.h diff --git a/requirements.txt b/requirements.txt index 1915d23228..dfc036df53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,19 +18,19 @@ fprime-fpl-convert-xml==1.0.3 fprime-fpl-extract-xml==1.0.3 fprime-fpl-layout==1.0.3 fprime-fpl-write-pic==1.0.3 -fprime-fpp-check==2.1.0 -fprime-fpp-depend==2.1.0 -fprime-fpp-filenames==2.1.0 -fprime-fpp-format==2.1.0 -fprime-fpp-from-xml==2.1.0 -fprime-fpp-locate-defs==2.1.0 -fprime-fpp-locate-uses==2.1.0 -fprime-fpp-syntax==2.1.0 -fprime-fpp-to-cpp==2.1.0 -fprime-fpp-to-dict==2.1.0 -fprime-fpp-to-json==2.1.0 -fprime-fpp-to-xml==2.1.0 -fprime-gds==v3.4.4a2 +fprime-fpp-check==2.2.0a3 +fprime-fpp-depend==2.2.0a3 +fprime-fpp-filenames==2.2.0a3 +fprime-fpp-format==2.2.0a3 +fprime-fpp-from-xml==2.2.0a3 +fprime-fpp-locate-defs==2.2.0a3 +fprime-fpp-locate-uses==2.2.0a3 +fprime-fpp-syntax==2.2.0a3 +fprime-fpp-to-cpp==2.2.0a3 +fprime-fpp-to-dict==2.2.0a3 +fprime-fpp-to-json==2.2.0a3 +fprime-fpp-to-xml==2.2.0a3 +fprime-gds==3.4.4a3 fprime-tools==v3.4.5a1 fprime-visual==1.0.2 gcovr==6.0