diff options
author | Matt Spinler <spinler@us.ibm.com> | 2019-12-12 10:35:01 -0600 |
---|---|---|
committer | Matt Spinler <spinler@us.ibm.com> | 2020-01-27 08:06:26 -0600 |
commit | 7d800a4e5d1305fa64ce02b99b0ec522bc454bbd (patch) | |
tree | 664b9dc6fed93d0637c7ee2f12d756f31b736d92 /test | |
parent | 5342d9ad13453e2fe9558b12137cd9be2e220544 (diff) | |
download | phosphor-logging-7d800a4e5d1305fa64ce02b99b0ec522bc454bbd.tar.gz phosphor-logging-7d800a4e5d1305fa64ce02b99b0ec522bc454bbd.zip |
PEL: On new PEL, send to host if necessary
When a new PEL comes in, send it to the host now if the host
is up and the class is currently idle.
The PLDM command will be dispatched in a standalone function
called from the event loop so that this function, which may
be part of the 'Create new log' D-Bus method response, can
return first.
Also added testcases to start verifying these paths now that
there is a full mock of the HostInterface class.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ib30a99cc61be9205122287ba310bc315d762e863
Diffstat (limited to 'test')
-rw-r--r-- | test/openpower-pels/host_notifier_test.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/test/openpower-pels/host_notifier_test.cpp b/test/openpower-pels/host_notifier_test.cpp index 3608539..afb5dc2 100644 --- a/test/openpower-pels/host_notifier_test.cpp +++ b/test/openpower-pels/host_notifier_test.cpp @@ -75,6 +75,27 @@ std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0) return pel; } +/** + * @brief Run an iteration of the event loop. + * + * An event loop is used for: + * 1) timer expiration callbacks + * 2) Dispatches + * 3) host interface receive callbacks + * + * @param[in] event - The event object + * @param[in] numEvents - number of times to call Event::run() + * @param[in] timeout - timeout value for run() + */ +void runEvents(sdeventplus::Event& event, size_t numEvents, + milliseconds timeout = milliseconds(1)) +{ + for (size_t i = 0; i < numEvents; i++) + { + event.run(timeout); + } +} + // Test that host state change callbacks work TEST_F(HostNotifierTest, TestHostStateChange) { @@ -281,3 +302,272 @@ TEST_F(HostNotifierTest, TestStartup) ASSERT_EQ(notifier.queueSize(), 20); } + +// Test the simple path were PELs get sent to the host +TEST_F(HostNotifierTest, TestSendCmd) +{ + Repository repo{repoPath}; + MockDataInterface dataIface; + + sdeventplus::Event sdEvent{event}; + + std::unique_ptr<HostInterface> hostIface = + std::make_unique<MockHostInterface>(event, dataIface); + + MockHostInterface& mockHostIface = + reinterpret_cast<MockHostInterface&>(*hostIface); + + HostNotifier notifier{repo, dataIface, std::move(hostIface)}; + + auto send = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(0); + }; + + EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _)) + .WillRepeatedly(Invoke(send)); + + // Add a PEL with the host off + auto pel = makePEL(); + repo.add(pel); + + EXPECT_EQ(notifier.queueSize(), 1); + + dataIface.changeHostState(true); + + runEvents(sdEvent, 1); + + // It was sent up + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1); + EXPECT_EQ(notifier.queueSize(), 0); + + // Verify the state was written to the PEL. + Repository::LogID id{Repository::LogID::Pel{pel->id()}}; + auto data = repo.getPELData(id); + PEL pelFromRepo{*data}; + EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent); + + // Add a few more PELs. They will get sent. + pel = makePEL(); + repo.add(pel); + + // Dispatch it by hitting the event loop (no commands sent yet) + // Don't need to test this step discretely in the future + runEvents(sdEvent, 1); + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1); + EXPECT_EQ(notifier.queueSize(), 0); + + // Send the command + runEvents(sdEvent, 1); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2); + EXPECT_EQ(notifier.queueSize(), 0); + + pel = makePEL(); + repo.add(pel); + + // dispatch and process the command + runEvents(sdEvent, 2); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3); + EXPECT_EQ(notifier.queueSize(), 0); +} + +// Test that if the class is created with the host up, +// it will send PELs +TEST_F(HostNotifierTest, TestStartAfterHostUp) +{ + Repository repo{repoPath}; + MockDataInterface dataIface; + + // Add PELs right away + auto pel = makePEL(); + repo.add(pel); + pel = makePEL(); + repo.add(pel); + + sdeventplus::Event sdEvent{event}; + + std::unique_ptr<HostInterface> hostIface = + std::make_unique<MockHostInterface>(event, dataIface); + + MockHostInterface& mockHostIface = + reinterpret_cast<MockHostInterface&>(*hostIface); + + auto send = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(0); + }; + + EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _)) + .WillRepeatedly(Invoke(send)); + + // Create the HostNotifier class with the host already up + dataIface.changeHostState(true); + HostNotifier notifier{repo, dataIface, std::move(hostIface)}; + + // It should start sending PELs right away + runEvents(sdEvent, 2); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2); + EXPECT_EQ(notifier.queueSize(), 0); +} + +// Test that a single failure will cause a retry +TEST_F(HostNotifierTest, TestHostRetry) +{ + Repository repo{repoPath}; + MockDataInterface dataIface; + + sdeventplus::Event sdEvent{event}; + + std::unique_ptr<HostInterface> hostIface = + std::make_unique<MockHostInterface>(event, dataIface); + + MockHostInterface& mockHostIface = + reinterpret_cast<MockHostInterface&>(*hostIface); + + HostNotifier notifier{repo, dataIface, std::move(hostIface)}; + + auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(1); + }; + auto sendSuccess = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(0); + }; + + EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _)) + .WillOnce(Invoke(sendFailure)) + .WillOnce(Invoke(sendSuccess)) + .WillOnce(Invoke(sendSuccess)); + + dataIface.changeHostState(true); + + auto pel = makePEL(); + repo.add(pel); + + // Dispatch and handle the command + runEvents(sdEvent, 2); + + // The command failed, so the queue isn't empty + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1); + EXPECT_EQ(notifier.queueSize(), 1); + + // Run the events again to let the timer expire and the + // command to be retried, which will be successful. + runEvents(sdEvent, 2, mockHostIface.getReceiveRetryDelay()); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2); + EXPECT_EQ(notifier.queueSize(), 0); + + // This one should pass with no problems + pel = makePEL(); + repo.add(pel); + + // Dispatch and handle the command + runEvents(sdEvent, 2); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3); + EXPECT_EQ(notifier.queueSize(), 0); +} + +// Test that all commands fail and notifier will give up +TEST_F(HostNotifierTest, TestHardFailure) +{ + Repository repo{repoPath}; + MockDataInterface dataIface; + + sdeventplus::Event sdEvent{event}; + + std::unique_ptr<HostInterface> hostIface = + std::make_unique<MockHostInterface>(event, dataIface); + + MockHostInterface& mockHostIface = + reinterpret_cast<MockHostInterface&>(*hostIface); + + HostNotifier notifier{repo, dataIface, std::move(hostIface)}; + + // Every call will fail + auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(1); + }; + + EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _)) + .WillRepeatedly(Invoke(sendFailure)); + + dataIface.changeHostState(true); + + auto pel = makePEL(); + repo.add(pel); + + // Clock more retries than necessary + runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay()); + + // Should have stopped after the 15 Tries + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 15); + EXPECT_EQ(notifier.queueSize(), 1); + + // Now add another PEL, and it should start trying again + // though it will also eventually give up + pel = makePEL(); + repo.add(pel); + + runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay()); + + // Tried an additional 15 times + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 30); + EXPECT_EQ(notifier.queueSize(), 2); +} + +// Cancel an in progress command +TEST_F(HostNotifierTest, TestCancelCmd) +{ + Repository repo{repoPath}; + MockDataInterface dataIface; + + sdeventplus::Event sdEvent{event}; + + std::unique_ptr<HostInterface> hostIface = + std::make_unique<MockHostInterface>(event, dataIface); + + MockHostInterface& mockHostIface = + reinterpret_cast<MockHostInterface&>(*hostIface); + + HostNotifier notifier{repo, dataIface, std::move(hostIface)}; + + auto send = [&mockHostIface](uint32_t id, uint32_t size) { + return mockHostIface.send(0); + }; + + EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _)) + .WillRepeatedly(Invoke(send)); + + dataIface.changeHostState(true); + + // Add and send one PEL, but don't enter the event loop + // so the receive function can't run. + auto pel = makePEL(); + repo.add(pel); + + // Not dispatched yet + EXPECT_EQ(notifier.queueSize(), 1); + + // Dispatch it + runEvents(sdEvent, 1); + + // It was sent and off the queue + EXPECT_EQ(notifier.queueSize(), 0); + + // This will cancel the receive + dataIface.changeHostState(false); + + // Back on the queue + EXPECT_EQ(notifier.queueSize(), 1); + + // Turn the host back on and make sure + // commands will work again + dataIface.changeHostState(true); + + runEvents(sdEvent, 1); + + EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1); + EXPECT_EQ(notifier.queueSize(), 0); +} |