#include "extensions/openpower-pels/data_interface.hpp" #include "extensions/openpower-pels/host_interface.hpp" #include #include #include #include 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)); MOCK_METHOD(bool, getHostPELEnablement, (), (const override)); MOCK_METHOD(std::string, getBMCState, (), (const override)); MOCK_METHOD(std::string, getChassisState, (), (const override)); MOCK_METHOD(std::string, getHostState, (), (const override)); MOCK_METHOD(std::string, getMotherboardCCIN, (), (const override)); MOCK_METHOD(void, getHWCalloutFields, (const std::string&, std::string&, std::string&, std::string&), (const override)); MOCK_METHOD(std::string, getLocationCode, (const std::string&), (const override)); MOCK_METHOD(std::vector, getSystemNames, (), (const override)); MOCK_METHOD(std::string, expandLocationCode, (const std::string&, uint16_t), (const override)); MOCK_METHOD(std::string, getInventoryFromLocCode, (const std::string&, uint16_t, bool), (const override)); MOCK_METHOD(void, assertLEDGroup, (const std::string&, bool), (const override)); MOCK_METHOD(void, setFunctional, (const std::string&, bool), (const override)); MOCK_METHOD(std::vector, getSystemIMKeyword, (), (const override)); MOCK_METHOD(bool, getQuiesceOnError, (), (const override)); void changeHostState(bool newState) { setHostUp(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( _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; } callResponseFunc(status); // Keep account of the number of commands responses for testing. _cmdsProcessed++; } private: /** * @brief The event source for the fifo */ std::unique_ptr _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