PEL: Mock the new PEL available cmd

Instead of using PLDM, just write to a FIFO in the filesystem where what
is written can be asynchronously read back from an event callback just
like how PLDM commands behave.

There is just a single byte written to the FIFO - a zero if the
command is considered successful, something nonzero to indicate
it should be a failure.

Change-Id: I1fd79782126a6b1555597a4642b9ca3bfd030a79
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index c2a84a0..dcce142 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -37,34 +37,188 @@
 
 /**
  * @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()
     {
-    }
-
-    virtual void cancelCmd() override
-    {
+        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 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;
 };