| #include "extensions/openpower-pels/data_interface.hpp" |
| #include "extensions/openpower-pels/host_interface.hpp" |
| |
| #include <fcntl.h> |
| |
| #include <filesystem> |
| #include <sdeventplus/source/io.hpp> |
| |
| #include <gmock/gmock.h> |
| |
| namespace openpower |
| { |
| namespace pels |
| { |
| |
| class MockDataInterface : public DataInterfaceBase |
| { |
| public: |
| MockDataInterface() |
| { |
| } |
| MOCK_METHOD(std::string, getMachineTypeModel, (), (const override)); |
| MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override)); |
| MOCK_METHOD(std::string, getServerFWVersion, (), (const override)); |
| MOCK_METHOD(std::string, getBMCFWVersion, (), (const override)); |
| MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override)); |
| |
| void changeHostState(bool newState) |
| { |
| setHostState(newState); |
| } |
| |
| void setHMCManaged(bool managed) |
| { |
| _hmcManaged = managed; |
| } |
| }; |
| |
| /** |
| * @brief The mock HostInterface class |
| * |
| * This replaces the PLDM calls with a FIFO for the asynchronous |
| * responses. |
| */ |
| class MockHostInterface : public HostInterface |
| { |
| public: |
| /** |
| * @brief Constructor |
| * |
| * @param[in] event - The sd_event object |
| * @param[in] dataIface - The DataInterface class |
| */ |
| MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) : |
| HostInterface(event, dataIface) |
| { |
| char templ[] = "/tmp/cmdfifoXXXXXX"; |
| std::filesystem::path dir = mkdtemp(templ); |
| _fifo = dir / "fifo"; |
| } |
| |
| /** |
| * @brief Destructor |
| */ |
| virtual ~MockHostInterface() |
| { |
| std::filesystem::remove_all(_fifo.parent_path()); |
| } |
| |
| MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override)); |
| |
| /** |
| * @brief Cancels waiting for a command response |
| */ |
| virtual void cancelCmd() override |
| { |
| _inProgress = false; |
| _source = nullptr; |
| } |
| |
| /** |
| * @brief Returns the amount of time to wait before retrying after |
| * a failed send command. |
| * |
| * @return milliseconds - The amount of time to wait |
| */ |
| virtual std::chrono::milliseconds getSendRetryDelay() const override |
| { |
| return std::chrono::milliseconds(2); |
| } |
| |
| /** |
| * @brief Returns the amount of time to wait before retrying after |
| * a command receive. |
| * |
| * @return milliseconds - The amount of time to wait |
| */ |
| virtual std::chrono::milliseconds getReceiveRetryDelay() const override |
| { |
| return std::chrono::milliseconds(2); |
| } |
| |
| /** |
| * @brief Returns the amount of time to wait before retrying if the |
| * host firmware's PEL storage was full and it can't store |
| * any more logs until it is freed up somehow. |
| * |
| * @return milliseconds - The amount of time to wait |
| */ |
| virtual std::chrono::milliseconds getHostFullRetryDelay() const override |
| { |
| return std::chrono::milliseconds(400); |
| } |
| |
| /** |
| * @brief Returns the number of commands processed |
| */ |
| size_t numCmdsProcessed() const |
| { |
| return _cmdsProcessed; |
| } |
| |
| /** |
| * @brief Writes the data passed in to the FIFO |
| * |
| * @param[in] hostResponse - use a 0 to indicate success |
| * |
| * @return CmdStatus - success or failure |
| */ |
| CmdStatus send(uint8_t hostResponse) |
| { |
| // Create a FIFO once. |
| if (!std::filesystem::exists(_fifo)) |
| { |
| if (mkfifo(_fifo.c_str(), 0622)) |
| { |
| ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno); |
| exit(-1); |
| } |
| } |
| |
| // Open it and register the reponse callback to |
| // be used on FD activity. |
| int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR); |
| EXPECT_TRUE(fd >= 0) << "Unable to open FIFO"; |
| |
| auto callback = [this](sdeventplus::source::IO& source, int fd, |
| uint32_t events) { |
| this->receive(source, fd, events); |
| }; |
| |
| try |
| { |
| _source = std::make_unique<sdeventplus::source::IO>( |
| _event, fd, EPOLLIN, |
| std::bind(callback, std::placeholders::_1, |
| std::placeholders::_2, std::placeholders::_3)); |
| } |
| catch (std::exception& e) |
| { |
| ADD_FAILURE() << "Event exception: " << e.what(); |
| close(fd); |
| return CmdStatus::failure; |
| } |
| |
| // Write the fake host reponse to the FIFO |
| auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse)); |
| EXPECT_EQ(bytesWritten, sizeof(hostResponse)); |
| |
| _inProgress = true; |
| |
| return CmdStatus::success; |
| } |
| |
| protected: |
| /** |
| * @brief Reads the data written to the fifo and then calls |
| * the subscriber's callback. |
| * |
| * Nonzero data indicates a command failure (for testing bad path). |
| * |
| * @param[in] source - The event source object |
| * @param[in] fd - The file descriptor used |
| * @param[in] events - The event bits |
| */ |
| void receive(sdeventplus::source::IO& source, int fd, |
| uint32_t events) override |
| { |
| if (!(events & EPOLLIN)) |
| { |
| return; |
| } |
| |
| _inProgress = false; |
| |
| int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY); |
| ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO"; |
| |
| // Read the host success/failure response from the FIFO. |
| uint8_t data; |
| auto bytesRead = read(newFD, &data, sizeof(data)); |
| EXPECT_EQ(bytesRead, sizeof(data)); |
| |
| close(newFD); |
| |
| ResponseStatus status = ResponseStatus::success; |
| if (data != 0) |
| { |
| status = ResponseStatus::failure; |
| } |
| |
| if (_responseFunc) |
| { |
| (*_responseFunc)(status); |
| } |
| |
| // Keep account of the number of commands responses for testing. |
| _cmdsProcessed++; |
| } |
| |
| private: |
| /** |
| * @brief The event source for the fifo |
| */ |
| std::unique_ptr<sdeventplus::source::IO> _source; |
| |
| /** |
| * @brief the path to the fifo |
| */ |
| std::filesystem::path _fifo; |
| |
| /** |
| * @brief The number of commands processed |
| */ |
| size_t _cmdsProcessed = 0; |
| }; |
| |
| } // namespace pels |
| } // namespace openpower |