xref: /openbmc/phosphor-logging/test/openpower-pels/host_notifier_test.cpp (revision 075c79237505ea3b810a461f5f514e4d520a0c44)
1f60ac27eSMatt Spinler /**
2f60ac27eSMatt Spinler  * Copyright © 2019 IBM Corporation
3f60ac27eSMatt Spinler  *
4f60ac27eSMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
5f60ac27eSMatt Spinler  * you may not use this file except in compliance with the License.
6f60ac27eSMatt Spinler  * You may obtain a copy of the License at
7f60ac27eSMatt Spinler  *
8f60ac27eSMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
9f60ac27eSMatt Spinler  *
10f60ac27eSMatt Spinler  * Unless required by applicable law or agreed to in writing, software
11f60ac27eSMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
12f60ac27eSMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f60ac27eSMatt Spinler  * See the License for the specific language governing permissions and
14f60ac27eSMatt Spinler  * limitations under the License.
15f60ac27eSMatt Spinler  */
16f60ac27eSMatt Spinler #include "extensions/openpower-pels/data_interface.hpp"
17f60ac27eSMatt Spinler #include "extensions/openpower-pels/host_notifier.hpp"
18f60ac27eSMatt Spinler #include "mocks.hpp"
19f60ac27eSMatt Spinler #include "pel_utils.hpp"
20f60ac27eSMatt Spinler 
21f60ac27eSMatt Spinler #include <fcntl.h>
22f60ac27eSMatt Spinler #include <sys/stat.h>
23f60ac27eSMatt Spinler #include <sys/types.h>
24f60ac27eSMatt Spinler 
25f60ac27eSMatt Spinler #include <chrono>
26f60ac27eSMatt Spinler 
27f60ac27eSMatt Spinler #include <gtest/gtest.h>
28f60ac27eSMatt Spinler 
29f60ac27eSMatt Spinler using namespace openpower::pels;
30f60ac27eSMatt Spinler using ::testing::_;
31f60ac27eSMatt Spinler using ::testing::Invoke;
3256e08263SMatt Spinler using ::testing::NiceMock;
33f60ac27eSMatt Spinler using ::testing::Return;
34f60ac27eSMatt Spinler namespace fs = std::filesystem;
35f60ac27eSMatt Spinler using namespace std::chrono;
36f60ac27eSMatt Spinler 
37f60ac27eSMatt Spinler const size_t actionFlags0Offset = 66;
38f60ac27eSMatt Spinler const size_t actionFlags1Offset = 67;
39f60ac27eSMatt Spinler 
40f60ac27eSMatt Spinler class HostNotifierTest : public CleanPELFiles
41f60ac27eSMatt Spinler {
42a943b15bSMatt Spinler   public:
HostNotifierTest()4356e08263SMatt Spinler     HostNotifierTest() : repo(repoPath)
44a943b15bSMatt Spinler     {
45a943b15bSMatt Spinler         auto r = sd_event_default(&event);
46a943b15bSMatt Spinler         EXPECT_TRUE(r >= 0);
4756e08263SMatt Spinler 
4824a8558bSMatt Spinler         ON_CALL(dataIface, getHostPELEnablement).WillByDefault(Return(true));
4924a8558bSMatt Spinler 
50*075c7923SPatrick Williams         hostIface =
51*075c7923SPatrick Williams             std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
5256e08263SMatt Spinler 
5356e08263SMatt Spinler         mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
5456e08263SMatt Spinler 
55d26fa3e7SPatrick Williams         auto send = [this](uint32_t /*id*/, uint32_t /*size*/) {
5656e08263SMatt Spinler             return this->mockHostIface->send(0);
5756e08263SMatt Spinler         };
5856e08263SMatt Spinler 
5956e08263SMatt Spinler         // Unless otherwise specified, sendNewLogCmd should always pass.
6056e08263SMatt Spinler         ON_CALL(*mockHostIface, sendNewLogCmd(_, _))
6156e08263SMatt Spinler             .WillByDefault(Invoke(send));
62a943b15bSMatt Spinler     }
63a943b15bSMatt Spinler 
~HostNotifierTest()64a943b15bSMatt Spinler     ~HostNotifierTest()
65a943b15bSMatt Spinler     {
66a943b15bSMatt Spinler         sd_event_unref(event);
67a943b15bSMatt Spinler     }
68a943b15bSMatt Spinler 
69a943b15bSMatt Spinler   protected:
70a943b15bSMatt Spinler     sd_event* event;
7156e08263SMatt Spinler     Repository repo;
7256e08263SMatt Spinler     NiceMock<MockDataInterface> dataIface;
7356e08263SMatt Spinler     std::unique_ptr<HostInterface> hostIface;
7456e08263SMatt Spinler     MockHostInterface* mockHostIface;
75f60ac27eSMatt Spinler };
76f60ac27eSMatt Spinler 
77f60ac27eSMatt Spinler /**
78f60ac27eSMatt Spinler  * @brief Create PEL with the specified action flags
79f60ac27eSMatt Spinler  *
80f60ac27eSMatt Spinler  * @param[in] actionFlagsMask - Optional action flags to use
81f60ac27eSMatt Spinler  *
82f60ac27eSMatt Spinler  * @return std::unique_ptr<PEL>
83f60ac27eSMatt Spinler  */
makePEL(uint16_t actionFlagsMask=0)84f60ac27eSMatt Spinler std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
85f60ac27eSMatt Spinler {
86f60ac27eSMatt Spinler     static uint32_t obmcID = 1;
87f60ac27eSMatt Spinler     auto data = pelDataFactory(TestPELType::pelSimple);
88f60ac27eSMatt Spinler 
89f60ac27eSMatt Spinler     data[actionFlags0Offset] |= actionFlagsMask >> 8;
90f60ac27eSMatt Spinler     data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
91f60ac27eSMatt Spinler 
92f60ac27eSMatt Spinler     auto pel = std::make_unique<PEL>(data, obmcID++);
93f60ac27eSMatt Spinler     pel->assignID();
94f60ac27eSMatt Spinler     pel->setCommitTime();
95f60ac27eSMatt Spinler     return pel;
96f60ac27eSMatt Spinler }
97f60ac27eSMatt Spinler 
987d800a4eSMatt Spinler /**
997d800a4eSMatt Spinler  * @brief Run an iteration of the event loop.
1007d800a4eSMatt Spinler  *
1017d800a4eSMatt Spinler  * An event loop is used for:
1027d800a4eSMatt Spinler  *   1) timer expiration callbacks
1037d800a4eSMatt Spinler  *   2) Dispatches
1047d800a4eSMatt Spinler  *   3) host interface receive callbacks
1057d800a4eSMatt Spinler  *
1067d800a4eSMatt Spinler  * @param[in] event - The event object
1077d800a4eSMatt Spinler  * @param[in] numEvents - number of times to call Event::run()
1087d800a4eSMatt Spinler  * @param[in] timeout - timeout value for run()
1097d800a4eSMatt Spinler  */
runEvents(sdeventplus::Event & event,size_t numEvents,milliseconds timeout=milliseconds (1))1107d800a4eSMatt Spinler void runEvents(sdeventplus::Event& event, size_t numEvents,
1117d800a4eSMatt Spinler                milliseconds timeout = milliseconds(1))
1127d800a4eSMatt Spinler {
1137d800a4eSMatt Spinler     for (size_t i = 0; i < numEvents; i++)
1147d800a4eSMatt Spinler     {
1157d800a4eSMatt Spinler         event.run(timeout);
1167d800a4eSMatt Spinler     }
1177d800a4eSMatt Spinler }
1187d800a4eSMatt Spinler 
119f60ac27eSMatt Spinler // Test that host state change callbacks work
TEST_F(HostNotifierTest,TestHostStateChange)120f60ac27eSMatt Spinler TEST_F(HostNotifierTest, TestHostStateChange)
121f60ac27eSMatt Spinler {
122f60ac27eSMatt Spinler     bool hostState = false;
123f60ac27eSMatt Spinler     bool called = false;
124*075c7923SPatrick Williams     DataInterfaceBase::HostStateChangeFunc func =
125*075c7923SPatrick Williams         [&hostState, &called](bool state) {
126f60ac27eSMatt Spinler             hostState = state;
127f60ac27eSMatt Spinler             called = true;
128f60ac27eSMatt Spinler         };
129f60ac27eSMatt Spinler 
130f60ac27eSMatt Spinler     dataIface.subscribeToHostStateChange("test", func);
131f60ac27eSMatt Spinler 
132f60ac27eSMatt Spinler     // callback called
133f60ac27eSMatt Spinler     dataIface.changeHostState(true);
134f60ac27eSMatt Spinler     EXPECT_TRUE(called);
135f60ac27eSMatt Spinler     EXPECT_TRUE(hostState);
136f60ac27eSMatt Spinler 
137f60ac27eSMatt Spinler     // No change, not called
138f60ac27eSMatt Spinler     called = false;
139f60ac27eSMatt Spinler     dataIface.changeHostState(true);
140f60ac27eSMatt Spinler     EXPECT_FALSE(called);
141f60ac27eSMatt Spinler 
142f60ac27eSMatt Spinler     // Called again
143f60ac27eSMatt Spinler     dataIface.changeHostState(false);
144f60ac27eSMatt Spinler     EXPECT_FALSE(hostState);
145f60ac27eSMatt Spinler     EXPECT_TRUE(called);
146f60ac27eSMatt Spinler 
147f60ac27eSMatt Spinler     // Shouldn't get called after an unsubscribe
148f60ac27eSMatt Spinler     dataIface.unsubscribeFromHostStateChange("test");
149f60ac27eSMatt Spinler 
150f60ac27eSMatt Spinler     called = false;
151f60ac27eSMatt Spinler 
152f60ac27eSMatt Spinler     dataIface.changeHostState(true);
153f60ac27eSMatt Spinler     EXPECT_FALSE(called);
154f60ac27eSMatt Spinler }
155f60ac27eSMatt Spinler 
156a943b15bSMatt Spinler // Test dealing with how acked PELs are put on the
157a943b15bSMatt Spinler // notification queue.
TEST_F(HostNotifierTest,TestPolicyAckedPEL)158a943b15bSMatt Spinler TEST_F(HostNotifierTest, TestPolicyAckedPEL)
159a943b15bSMatt Spinler {
160a943b15bSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
161a943b15bSMatt Spinler 
162a943b15bSMatt Spinler     auto pel = makePEL();
163a943b15bSMatt Spinler     repo.add(pel);
164a943b15bSMatt Spinler 
165a943b15bSMatt Spinler     // This is required
166a943b15bSMatt Spinler     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
167f77debb9SMatt Spinler     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
168a943b15bSMatt Spinler 
169a943b15bSMatt Spinler     // Not in the repo
170a943b15bSMatt Spinler     EXPECT_FALSE(notifier.enqueueRequired(42));
171f77debb9SMatt Spinler     EXPECT_FALSE(notifier.notifyRequired(42));
172a943b15bSMatt Spinler 
173a943b15bSMatt Spinler     // Now set this PEL to host acked
174a943b15bSMatt Spinler     repo.setPELHostTransState(pel->id(), TransmissionState::acked);
175a943b15bSMatt Spinler 
176a943b15bSMatt Spinler     // Since it's acked, doesn't need to be enqueued or transmitted
177a943b15bSMatt Spinler     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
178f77debb9SMatt Spinler     EXPECT_FALSE(notifier.notifyRequired(pel->id()));
179a943b15bSMatt Spinler }
180a943b15bSMatt Spinler 
181a943b15bSMatt Spinler // Test the 'don't report' PEL flag
TEST_F(HostNotifierTest,TestPolicyDontReport)182a943b15bSMatt Spinler TEST_F(HostNotifierTest, TestPolicyDontReport)
183a943b15bSMatt Spinler {
184a943b15bSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
185a943b15bSMatt Spinler 
186a943b15bSMatt Spinler     // dontReportToHostFlagBit
187a943b15bSMatt Spinler     auto pel = makePEL(0x1000);
188a943b15bSMatt Spinler 
189a943b15bSMatt Spinler     // Double check the action flag is still set
190a943b15bSMatt Spinler     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
191a943b15bSMatt Spinler     EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
192a943b15bSMatt Spinler 
193a943b15bSMatt Spinler     repo.add(pel);
194a943b15bSMatt Spinler 
195a943b15bSMatt Spinler     // Don't need to send this to the host
196a943b15bSMatt Spinler     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
197a943b15bSMatt Spinler }
198a943b15bSMatt Spinler 
199a943b15bSMatt Spinler // Test that hidden PELs need notification when there
200a943b15bSMatt Spinler // is no HMC.
TEST_F(HostNotifierTest,TestPolicyHiddenNoHMC)201a943b15bSMatt Spinler TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
202a943b15bSMatt Spinler {
203a943b15bSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
204a943b15bSMatt Spinler 
205a943b15bSMatt Spinler     // hiddenFlagBit
206a943b15bSMatt Spinler     auto pel = makePEL(0x4000);
207a943b15bSMatt Spinler 
208a943b15bSMatt Spinler     // Double check the action flag is still set
209a943b15bSMatt Spinler     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
210a943b15bSMatt Spinler     EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
211a943b15bSMatt Spinler 
212a943b15bSMatt Spinler     repo.add(pel);
213a943b15bSMatt Spinler 
214a943b15bSMatt Spinler     // Still need to enqueue this
215a943b15bSMatt Spinler     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
216f77debb9SMatt Spinler 
217f77debb9SMatt Spinler     // Still need to send it
218f77debb9SMatt Spinler     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
219a943b15bSMatt Spinler }
220a943b15bSMatt Spinler 
221a943b15bSMatt Spinler // Don't need to enqueue a hidden log already acked by the HMC
TEST_F(HostNotifierTest,TestPolicyHiddenWithHMCAcked)222a943b15bSMatt Spinler TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
223a943b15bSMatt Spinler {
224a943b15bSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
225a943b15bSMatt Spinler 
226a943b15bSMatt Spinler     // hiddenFlagBit
227a943b15bSMatt Spinler     auto pel = makePEL(0x4000);
228a943b15bSMatt Spinler 
229a943b15bSMatt Spinler     // Double check the action flag is still set
230a943b15bSMatt Spinler     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
231a943b15bSMatt Spinler     EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
232a943b15bSMatt Spinler 
233a943b15bSMatt Spinler     repo.add(pel);
234a943b15bSMatt Spinler 
235a943b15bSMatt Spinler     // No HMC yet, so required
236a943b15bSMatt Spinler     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
237a943b15bSMatt Spinler 
238a943b15bSMatt Spinler     repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
239a943b15bSMatt Spinler 
240a943b15bSMatt Spinler     // Not required anymore
241a943b15bSMatt Spinler     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
242a943b15bSMatt Spinler }
243a943b15bSMatt Spinler 
244f77debb9SMatt Spinler // Test that changing the HMC manage status affects
245f77debb9SMatt Spinler // the policy with hidden log notification.
TEST_F(HostNotifierTest,TestPolicyHiddenWithHMCManaged)246f77debb9SMatt Spinler TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
247f77debb9SMatt Spinler {
248f77debb9SMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
249f77debb9SMatt Spinler 
250f77debb9SMatt Spinler     // hiddenFlagBit
251f77debb9SMatt Spinler     auto pel = makePEL(0x4000);
252f77debb9SMatt Spinler 
253f77debb9SMatt Spinler     repo.add(pel);
254f77debb9SMatt Spinler 
255f77debb9SMatt Spinler     // The first time, the HMC managed is false
256f77debb9SMatt Spinler     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
257f77debb9SMatt Spinler 
258f77debb9SMatt Spinler     dataIface.setHMCManaged(true);
259f77debb9SMatt Spinler 
260f77debb9SMatt Spinler     // This time, HMC managed is true so no need to notify
261f77debb9SMatt Spinler     EXPECT_FALSE(notifier.notifyRequired(pel->id()));
262f77debb9SMatt Spinler }
263f77debb9SMatt Spinler 
264f60ac27eSMatt Spinler // Test that PELs are enqueued on startup
TEST_F(HostNotifierTest,TestStartup)265f60ac27eSMatt Spinler TEST_F(HostNotifierTest, TestStartup)
266f60ac27eSMatt Spinler {
267f60ac27eSMatt Spinler     // Give the repo 10 PELs to start with
268f60ac27eSMatt Spinler     for (int i = 0; i < 10; i++)
269f60ac27eSMatt Spinler     {
270f60ac27eSMatt Spinler         auto pel = makePEL();
271f60ac27eSMatt Spinler         repo.add(pel);
272f60ac27eSMatt Spinler     }
273f60ac27eSMatt Spinler 
274f60ac27eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
275f60ac27eSMatt Spinler 
276f60ac27eSMatt Spinler     ASSERT_EQ(notifier.queueSize(), 10);
277f60ac27eSMatt Spinler 
278f60ac27eSMatt Spinler     // Now add 10 more after the notifier is watching
279f60ac27eSMatt Spinler     for (int i = 0; i < 10; i++)
280f60ac27eSMatt Spinler     {
281f60ac27eSMatt Spinler         auto pel = makePEL();
282f60ac27eSMatt Spinler         repo.add(pel);
283f60ac27eSMatt Spinler     }
284f60ac27eSMatt Spinler 
285f60ac27eSMatt Spinler     ASSERT_EQ(notifier.queueSize(), 20);
286f60ac27eSMatt Spinler }
2877d800a4eSMatt Spinler 
2887d800a4eSMatt Spinler // Test the simple path were PELs get sent to the host
TEST_F(HostNotifierTest,TestSendCmd)2897d800a4eSMatt Spinler TEST_F(HostNotifierTest, TestSendCmd)
2907d800a4eSMatt Spinler {
2917d800a4eSMatt Spinler     sdeventplus::Event sdEvent{event};
2927d800a4eSMatt Spinler 
2937d800a4eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
2947d800a4eSMatt Spinler 
2957d800a4eSMatt Spinler     // Add a PEL with the host off
2967d800a4eSMatt Spinler     auto pel = makePEL();
2977d800a4eSMatt Spinler     repo.add(pel);
2987d800a4eSMatt Spinler 
2997d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
3007d800a4eSMatt Spinler 
3017d800a4eSMatt Spinler     dataIface.changeHostState(true);
3027d800a4eSMatt Spinler 
303e5f7508bSMatt Spinler     runEvents(sdEvent, 2);
3047d800a4eSMatt Spinler 
3057d800a4eSMatt Spinler     // It was sent up
30656e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
3077d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
3087d800a4eSMatt Spinler 
3097d800a4eSMatt Spinler     // Verify the state was written to the PEL.
3107d800a4eSMatt Spinler     Repository::LogID id{Repository::LogID::Pel{pel->id()}};
3117d800a4eSMatt Spinler     auto data = repo.getPELData(id);
3127d800a4eSMatt Spinler     PEL pelFromRepo{*data};
3137d800a4eSMatt Spinler     EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
3147d800a4eSMatt Spinler 
3157d800a4eSMatt Spinler     // Add a few more PELs.  They will get sent.
3167d800a4eSMatt Spinler     pel = makePEL();
3177d800a4eSMatt Spinler     repo.add(pel);
3187d800a4eSMatt Spinler 
3197d800a4eSMatt Spinler     // Dispatch it by hitting the event loop (no commands sent yet)
3207d800a4eSMatt Spinler     // Don't need to test this step discretely in the future
3217d800a4eSMatt Spinler     runEvents(sdEvent, 1);
32256e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
3237d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
3247d800a4eSMatt Spinler 
3257d800a4eSMatt Spinler     // Send the command
3267d800a4eSMatt Spinler     runEvents(sdEvent, 1);
3277d800a4eSMatt Spinler 
32856e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
3297d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
3307d800a4eSMatt Spinler 
3317d800a4eSMatt Spinler     pel = makePEL();
3327d800a4eSMatt Spinler     repo.add(pel);
3337d800a4eSMatt Spinler 
3347d800a4eSMatt Spinler     // dispatch and process the command
3357d800a4eSMatt Spinler     runEvents(sdEvent, 2);
3367d800a4eSMatt Spinler 
33756e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
3387d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
3397d800a4eSMatt Spinler }
3407d800a4eSMatt Spinler 
3417d800a4eSMatt Spinler // Test that if the class is created with the host up,
3427d800a4eSMatt Spinler // it will send PELs
TEST_F(HostNotifierTest,TestStartAfterHostUp)3437d800a4eSMatt Spinler TEST_F(HostNotifierTest, TestStartAfterHostUp)
3447d800a4eSMatt Spinler {
3457d800a4eSMatt Spinler     // Add PELs right away
3467d800a4eSMatt Spinler     auto pel = makePEL();
3477d800a4eSMatt Spinler     repo.add(pel);
3487d800a4eSMatt Spinler     pel = makePEL();
3497d800a4eSMatt Spinler     repo.add(pel);
3507d800a4eSMatt Spinler 
3517d800a4eSMatt Spinler     sdeventplus::Event sdEvent{event};
3527d800a4eSMatt Spinler 
3537d800a4eSMatt Spinler     // Create the HostNotifier class with the host already up
3547d800a4eSMatt Spinler     dataIface.changeHostState(true);
3557d800a4eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
3567d800a4eSMatt Spinler 
3577d800a4eSMatt Spinler     // It should start sending PELs right away
358e5f7508bSMatt Spinler     runEvents(sdEvent, 3);
3597d800a4eSMatt Spinler 
36056e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
3617d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
3627d800a4eSMatt Spinler }
3637d800a4eSMatt Spinler 
3647d800a4eSMatt Spinler // Test that a single failure will cause a retry
TEST_F(HostNotifierTest,TestHostRetry)3657d800a4eSMatt Spinler TEST_F(HostNotifierTest, TestHostRetry)
3667d800a4eSMatt Spinler {
3677d800a4eSMatt Spinler     sdeventplus::Event sdEvent{event};
3687d800a4eSMatt Spinler 
3697d800a4eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
3707d800a4eSMatt Spinler 
371d26fa3e7SPatrick Williams     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
37256e08263SMatt Spinler         return this->mockHostIface->send(1);
3737d800a4eSMatt Spinler     };
374d26fa3e7SPatrick Williams     auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
37556e08263SMatt Spinler         return this->mockHostIface->send(0);
3767d800a4eSMatt Spinler     };
3777d800a4eSMatt Spinler 
37856e08263SMatt Spinler     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
3797d800a4eSMatt Spinler         .WillOnce(Invoke(sendFailure))
3807d800a4eSMatt Spinler         .WillOnce(Invoke(sendSuccess))
3817d800a4eSMatt Spinler         .WillOnce(Invoke(sendSuccess));
3827d800a4eSMatt Spinler 
3837d800a4eSMatt Spinler     dataIface.changeHostState(true);
3847d800a4eSMatt Spinler 
3857d800a4eSMatt Spinler     auto pel = makePEL();
3867d800a4eSMatt Spinler     repo.add(pel);
3877d800a4eSMatt Spinler 
3887d800a4eSMatt Spinler     // Dispatch and handle the command
3897d800a4eSMatt Spinler     runEvents(sdEvent, 2);
3907d800a4eSMatt Spinler 
3917d800a4eSMatt Spinler     // The command failed, so the queue isn't empty
39256e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
3937d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
3947d800a4eSMatt Spinler 
3957d800a4eSMatt Spinler     // Run the events again to let the timer expire and the
3967d800a4eSMatt Spinler     // command to be retried, which will be successful.
39756e08263SMatt Spinler     runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
3987d800a4eSMatt Spinler 
39956e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
4007d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
4017d800a4eSMatt Spinler 
4027d800a4eSMatt Spinler     // This one should pass with no problems
4037d800a4eSMatt Spinler     pel = makePEL();
4047d800a4eSMatt Spinler     repo.add(pel);
4057d800a4eSMatt Spinler 
4067d800a4eSMatt Spinler     // Dispatch and handle the command
4077d800a4eSMatt Spinler     runEvents(sdEvent, 2);
4087d800a4eSMatt Spinler 
40956e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
4107d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
4117d800a4eSMatt Spinler }
4127d800a4eSMatt Spinler 
4137d800a4eSMatt Spinler // Test that all commands fail and notifier will give up
TEST_F(HostNotifierTest,TestHardFailure)4147d800a4eSMatt Spinler TEST_F(HostNotifierTest, TestHardFailure)
4157d800a4eSMatt Spinler {
4167d800a4eSMatt Spinler     sdeventplus::Event sdEvent{event};
4177d800a4eSMatt Spinler 
4187d800a4eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
4197d800a4eSMatt Spinler 
4207d800a4eSMatt Spinler     // Every call will fail
421d26fa3e7SPatrick Williams     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
42256e08263SMatt Spinler         return this->mockHostIface->send(1);
4237d800a4eSMatt Spinler     };
4247d800a4eSMatt Spinler 
42556e08263SMatt Spinler     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
4267d800a4eSMatt Spinler         .WillRepeatedly(Invoke(sendFailure));
4277d800a4eSMatt Spinler 
4287d800a4eSMatt Spinler     dataIface.changeHostState(true);
4297d800a4eSMatt Spinler 
4307d800a4eSMatt Spinler     auto pel = makePEL();
4317d800a4eSMatt Spinler     repo.add(pel);
4327d800a4eSMatt Spinler 
4337d800a4eSMatt Spinler     // Clock more retries than necessary
43456e08263SMatt Spinler     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
4357d800a4eSMatt Spinler 
4367d800a4eSMatt Spinler     // Should have stopped after the 15 Tries
43756e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
4387d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
4397d800a4eSMatt Spinler 
4407d800a4eSMatt Spinler     // Now add another PEL, and it should start trying again
4417d800a4eSMatt Spinler     // though it will also eventually give up
4427d800a4eSMatt Spinler     pel = makePEL();
4437d800a4eSMatt Spinler     repo.add(pel);
4447d800a4eSMatt Spinler 
44556e08263SMatt Spinler     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
4467d800a4eSMatt Spinler 
4477d800a4eSMatt Spinler     // Tried an additional 15 times
44856e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
4497d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 2);
4507d800a4eSMatt Spinler }
4517d800a4eSMatt Spinler 
452527ff346SMatt Spinler // Test that if the command cannot be started it will give
453527ff346SMatt Spinler // up but still try again later
TEST_F(HostNotifierTest,TestCannotStartCmd)454527ff346SMatt Spinler TEST_F(HostNotifierTest, TestCannotStartCmd)
455527ff346SMatt Spinler {
456527ff346SMatt Spinler     sdeventplus::Event sdEvent{event};
457527ff346SMatt Spinler 
458527ff346SMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
459527ff346SMatt Spinler 
460527ff346SMatt Spinler     // Make it behave like startCommand() fails.
461527ff346SMatt Spinler     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
462527ff346SMatt Spinler         this->mockHostIface->cancelCmd();
463527ff346SMatt Spinler         return CmdStatus::failure;
464527ff346SMatt Spinler     };
465527ff346SMatt Spinler 
466527ff346SMatt Spinler     auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
467527ff346SMatt Spinler         return this->mockHostIface->send(0);
468527ff346SMatt Spinler     };
469527ff346SMatt Spinler 
470527ff346SMatt Spinler     // Fails 16 times (1 fail + 15  retries) and
471527ff346SMatt Spinler     // then start working.
472527ff346SMatt Spinler     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
473527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
474527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
475527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
476527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
477527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
478527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
479527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
480527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
481527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
482527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
483527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
484527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
485527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
486527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
487527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
488527ff346SMatt Spinler         .WillOnce(Invoke(sendFailure))
489527ff346SMatt Spinler         .WillRepeatedly(Invoke(sendSuccess));
490527ff346SMatt Spinler 
491527ff346SMatt Spinler     dataIface.changeHostState(true);
492527ff346SMatt Spinler 
493527ff346SMatt Spinler     auto pel = makePEL();
494527ff346SMatt Spinler     repo.add(pel);
495527ff346SMatt Spinler 
496527ff346SMatt Spinler     // Clock more retries than necessary
497527ff346SMatt Spinler     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
498527ff346SMatt Spinler 
499527ff346SMatt Spinler     // Didn't get far enough for a cmd to be processed
500527ff346SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 0);
501527ff346SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
502527ff346SMatt Spinler 
503527ff346SMatt Spinler     // At this point, commands will work again.
504527ff346SMatt Spinler 
505527ff346SMatt Spinler     pel = makePEL();
506527ff346SMatt Spinler     repo.add(pel);
507527ff346SMatt Spinler 
508527ff346SMatt Spinler     // Run the events to send the PELs
509527ff346SMatt Spinler     runEvents(sdEvent, 5, mockHostIface->getReceiveRetryDelay());
510527ff346SMatt Spinler 
511527ff346SMatt Spinler     // All PELs sent
512527ff346SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
513527ff346SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
514527ff346SMatt Spinler }
515527ff346SMatt Spinler 
5167d800a4eSMatt Spinler // Cancel an in progress command
TEST_F(HostNotifierTest,TestCancelCmd)5177d800a4eSMatt Spinler TEST_F(HostNotifierTest, TestCancelCmd)
5187d800a4eSMatt Spinler {
5197d800a4eSMatt Spinler     sdeventplus::Event sdEvent{event};
5207d800a4eSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
5217d800a4eSMatt Spinler 
5227d800a4eSMatt Spinler     dataIface.changeHostState(true);
5237d800a4eSMatt Spinler 
5247d800a4eSMatt Spinler     // Add and send one PEL, but don't enter the event loop
5257d800a4eSMatt Spinler     // so the receive function can't run.
5267d800a4eSMatt Spinler     auto pel = makePEL();
5277d800a4eSMatt Spinler     repo.add(pel);
5287d800a4eSMatt Spinler 
5297d800a4eSMatt Spinler     // Not dispatched yet
5307d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
5317d800a4eSMatt Spinler 
5327d800a4eSMatt Spinler     // Dispatch it
533e5f7508bSMatt Spinler     runEvents(sdEvent, 2);
5347d800a4eSMatt Spinler 
5357d800a4eSMatt Spinler     // It was sent and off the queue
5367d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
5377d800a4eSMatt Spinler 
5387d800a4eSMatt Spinler     // This will cancel the receive
5397d800a4eSMatt Spinler     dataIface.changeHostState(false);
5407d800a4eSMatt Spinler 
5417d800a4eSMatt Spinler     // Back on the queue
5427d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
5437d800a4eSMatt Spinler 
5447d800a4eSMatt Spinler     // Turn the host back on and make sure
5457d800a4eSMatt Spinler     // commands will work again
5467d800a4eSMatt Spinler     dataIface.changeHostState(true);
5477d800a4eSMatt Spinler 
5487d800a4eSMatt Spinler     runEvents(sdEvent, 1);
5497d800a4eSMatt Spinler 
55056e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
5517d800a4eSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
5527d800a4eSMatt Spinler }
553cc3b64aeSMatt Spinler 
554cc3b64aeSMatt Spinler // Test that acking a PEL persist across power cycles
TEST_F(HostNotifierTest,TestPowerCycleAndAcks)555cc3b64aeSMatt Spinler TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
556cc3b64aeSMatt Spinler {
557cc3b64aeSMatt Spinler     sdeventplus::Event sdEvent{event};
558cc3b64aeSMatt Spinler 
559cc3b64aeSMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
560cc3b64aeSMatt Spinler 
561cc3b64aeSMatt Spinler     // Add 2 PELs with host off
562cc3b64aeSMatt Spinler     auto pel = makePEL();
563cc3b64aeSMatt Spinler     repo.add(pel);
564cc3b64aeSMatt Spinler     auto id1 = pel->id();
565cc3b64aeSMatt Spinler 
566cc3b64aeSMatt Spinler     pel = makePEL();
567cc3b64aeSMatt Spinler     repo.add(pel);
568cc3b64aeSMatt Spinler     auto id2 = pel->id();
569cc3b64aeSMatt Spinler 
570cc3b64aeSMatt Spinler     dataIface.changeHostState(true);
571cc3b64aeSMatt Spinler 
572e5f7508bSMatt Spinler     runEvents(sdEvent, 3);
573cc3b64aeSMatt Spinler 
574cc3b64aeSMatt Spinler     // The were both sent.
57556e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
576cc3b64aeSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
577cc3b64aeSMatt Spinler 
578cc3b64aeSMatt Spinler     dataIface.changeHostState(false);
579cc3b64aeSMatt Spinler 
580cc3b64aeSMatt Spinler     // Those PELs weren't acked, so they will get sent again
581cc3b64aeSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 2);
582cc3b64aeSMatt Spinler 
583cc3b64aeSMatt Spinler     // Power back on and send them again
584cc3b64aeSMatt Spinler     dataIface.changeHostState(true);
585e5f7508bSMatt Spinler     runEvents(sdEvent, 3);
586cc3b64aeSMatt Spinler 
58756e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
588cc3b64aeSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
589cc3b64aeSMatt Spinler 
590cc3b64aeSMatt Spinler     // Ack them and verify the state in the PEL.
591cc3b64aeSMatt Spinler     notifier.ackPEL(id1);
592cc3b64aeSMatt Spinler     notifier.ackPEL(id2);
593cc3b64aeSMatt Spinler 
594cc3b64aeSMatt Spinler     Repository::LogID id{Repository::LogID::Pel{id1}};
595cc3b64aeSMatt Spinler     auto data = repo.getPELData(id);
596cc3b64aeSMatt Spinler     PEL pelFromRepo1{*data};
597cc3b64aeSMatt Spinler     EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
598cc3b64aeSMatt Spinler 
599cc3b64aeSMatt Spinler     id.pelID.id = id2;
600cc3b64aeSMatt Spinler     data = repo.getPELData(id);
601cc3b64aeSMatt Spinler     PEL pelFromRepo2{*data};
602cc3b64aeSMatt Spinler     EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
603cc3b64aeSMatt Spinler 
604cc3b64aeSMatt Spinler     // Power back off, and they should't get re-added
605cc3b64aeSMatt Spinler     dataIface.changeHostState(false);
606cc3b64aeSMatt Spinler 
607cc3b64aeSMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
608cc3b64aeSMatt Spinler }
60941293cb8SMatt Spinler 
61041293cb8SMatt Spinler // Test the host full condition
TEST_F(HostNotifierTest,TestHostFull)61141293cb8SMatt Spinler TEST_F(HostNotifierTest, TestHostFull)
61241293cb8SMatt Spinler {
61341293cb8SMatt Spinler     // The full interaction with the host is:
61441293cb8SMatt Spinler     // BMC:  new PEL available
61541293cb8SMatt Spinler     // Host: ReadPELFile  (not modeled here)
61641293cb8SMatt Spinler     // Host: Ack(id) (if not full), or HostFull(id)
61741293cb8SMatt Spinler     // BMC: if full and any new PELs come in, don't sent them
61841293cb8SMatt Spinler     // Start a timer and try again
61941293cb8SMatt Spinler     // Host responds with either Ack or full
62041293cb8SMatt Spinler     // and repeat
62141293cb8SMatt Spinler 
62241293cb8SMatt Spinler     sdeventplus::Event sdEvent{event};
62341293cb8SMatt Spinler     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
62441293cb8SMatt Spinler 
62541293cb8SMatt Spinler     dataIface.changeHostState(true);
62641293cb8SMatt Spinler 
62741293cb8SMatt Spinler     // Add and dispatch/send one PEL
62841293cb8SMatt Spinler     auto pel = makePEL();
62941293cb8SMatt Spinler     auto id = pel->id();
63041293cb8SMatt Spinler     repo.add(pel);
63141293cb8SMatt Spinler     runEvents(sdEvent, 2);
63241293cb8SMatt Spinler 
63356e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
63441293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
63541293cb8SMatt Spinler 
63641293cb8SMatt Spinler     // Host is full
63741293cb8SMatt Spinler     notifier.setHostFull(id);
63841293cb8SMatt Spinler 
63941293cb8SMatt Spinler     // It goes back on the queue
64041293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
64141293cb8SMatt Spinler 
64241293cb8SMatt Spinler     // The transmission state goes back to new
64341293cb8SMatt Spinler     Repository::LogID i{Repository::LogID::Pel{id}};
64441293cb8SMatt Spinler     auto data = repo.getPELData(i);
64541293cb8SMatt Spinler     PEL pelFromRepo{*data};
64641293cb8SMatt Spinler     EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
64741293cb8SMatt Spinler 
64841293cb8SMatt Spinler     // Clock it, nothing should be sent still.
64941293cb8SMatt Spinler     runEvents(sdEvent, 1);
65041293cb8SMatt Spinler 
65156e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
65241293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
65341293cb8SMatt Spinler 
65441293cb8SMatt Spinler     // Add another PEL and clock it, still nothing sent
65541293cb8SMatt Spinler     pel = makePEL();
65641293cb8SMatt Spinler     repo.add(pel);
65741293cb8SMatt Spinler     runEvents(sdEvent, 2);
65856e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
65941293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 2);
66041293cb8SMatt Spinler 
66141293cb8SMatt Spinler     // Let the host full timer expire to trigger a retry.
66241293cb8SMatt Spinler     // Add some extra event passes just to be sure nothing new is sent.
66356e08263SMatt Spinler     runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
66441293cb8SMatt Spinler 
66541293cb8SMatt Spinler     // The timer expiration will send just the 1, not both
66656e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
66741293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 1);
66841293cb8SMatt Spinler 
66941293cb8SMatt Spinler     // Host still full
67041293cb8SMatt Spinler     notifier.setHostFull(id);
67141293cb8SMatt Spinler 
67241293cb8SMatt Spinler     // Let the host full timer attempt again
67356e08263SMatt Spinler     runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
67456e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
67541293cb8SMatt Spinler 
67641293cb8SMatt Spinler     // Add yet another PEL with the retry timer expired.
67741293cb8SMatt Spinler     // It shouldn't get sent out.
67841293cb8SMatt Spinler     pel = makePEL();
67941293cb8SMatt Spinler     repo.add(pel);
68041293cb8SMatt Spinler     runEvents(sdEvent, 2);
68156e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
68241293cb8SMatt Spinler 
68341293cb8SMatt Spinler     // The last 2 PELs still on the queue
68441293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 2);
68541293cb8SMatt Spinler 
68641293cb8SMatt Spinler     // Host no longer full, it finally acks the first PEL
68741293cb8SMatt Spinler     notifier.ackPEL(id);
68841293cb8SMatt Spinler 
68941293cb8SMatt Spinler     // Now the remaining 2 PELs will be dispatched
69041293cb8SMatt Spinler     runEvents(sdEvent, 3);
69141293cb8SMatt Spinler 
69256e08263SMatt Spinler     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
69341293cb8SMatt Spinler     EXPECT_EQ(notifier.queueSize(), 0);
69441293cb8SMatt Spinler }
695a19b6234SMatt Spinler 
696a19b6234SMatt Spinler // Test when the host says it was send a malformed PEL
TEST_F(HostNotifierTest,TestBadPEL)697a19b6234SMatt Spinler TEST_F(HostNotifierTest, TestBadPEL)
698a19b6234SMatt Spinler {
699a19b6234SMatt Spinler     sdeventplus::Event sdEvent{event};
700a19b6234SMatt Spinler 
701a19b6234SMatt Spinler     {
70256e08263SMatt Spinler         Repository repo1{repoPath};
70356e08263SMatt Spinler         HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
704a19b6234SMatt Spinler 
705a19b6234SMatt Spinler         dataIface.changeHostState(true);
706a19b6234SMatt Spinler 
707a19b6234SMatt Spinler         // Add a PEL and dispatch and send it
708a19b6234SMatt Spinler         auto pel = makePEL();
709a19b6234SMatt Spinler         auto id = pel->id();
71056e08263SMatt Spinler         repo1.add(pel);
711a19b6234SMatt Spinler 
712a19b6234SMatt Spinler         runEvents(sdEvent, 2);
71356e08263SMatt Spinler         EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
714a19b6234SMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
715a19b6234SMatt Spinler 
716a19b6234SMatt Spinler         // The host rejected it.
717a19b6234SMatt Spinler         notifier.setBadPEL(id);
718a19b6234SMatt Spinler 
719a19b6234SMatt Spinler         // Doesn't go back on the queue
720a19b6234SMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
721a19b6234SMatt Spinler 
722a19b6234SMatt Spinler         // Check the state was saved in the PEL itself
723a19b6234SMatt Spinler         Repository::LogID i{Repository::LogID::Pel{id}};
72456e08263SMatt Spinler         auto data = repo1.getPELData(i);
725a19b6234SMatt Spinler         PEL pelFromRepo{*data};
726a19b6234SMatt Spinler         EXPECT_EQ(pelFromRepo.hostTransmissionState(),
727a19b6234SMatt Spinler                   TransmissionState::badPEL);
728a19b6234SMatt Spinler 
729a19b6234SMatt Spinler         dataIface.changeHostState(false);
730a19b6234SMatt Spinler 
731a19b6234SMatt Spinler         // Ensure it doesn't go back on the queue on a power cycle
732a19b6234SMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
733a19b6234SMatt Spinler     }
734a19b6234SMatt Spinler 
735a19b6234SMatt Spinler     // Now restore the repo, and make sure it doesn't come back
736a19b6234SMatt Spinler     {
73756e08263SMatt Spinler         Repository repo1{repoPath};
738a19b6234SMatt Spinler 
73956e08263SMatt Spinler         std::unique_ptr<HostInterface> hostIface1 =
740a19b6234SMatt Spinler             std::make_unique<MockHostInterface>(event, dataIface);
741a19b6234SMatt Spinler 
74256e08263SMatt Spinler         HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
743a19b6234SMatt Spinler 
744a19b6234SMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
745a19b6234SMatt Spinler     }
746a19b6234SMatt Spinler }
74724a8558bSMatt Spinler 
74824a8558bSMatt Spinler // Test that sending PELs can be disabled
TEST_F(HostNotifierTest,TestDisable)74924a8558bSMatt Spinler TEST_F(HostNotifierTest, TestDisable)
75024a8558bSMatt Spinler {
75124a8558bSMatt Spinler     // Turn off sending the PELs except for once in the middle
75224a8558bSMatt Spinler     EXPECT_CALL(dataIface, getHostPELEnablement())
75324a8558bSMatt Spinler         .WillOnce(Return(false))
75424a8558bSMatt Spinler         .WillOnce(Return(false))
75524a8558bSMatt Spinler         .WillOnce(Return(true))
75624a8558bSMatt Spinler         .WillOnce(Return(false))
75724a8558bSMatt Spinler         .WillOnce(Return(false))
75824a8558bSMatt Spinler         .WillOnce(Return(false));
75924a8558bSMatt Spinler 
76024a8558bSMatt Spinler     {
76124a8558bSMatt Spinler         HostNotifier notifier{repo, dataIface, std::move(hostIface)};
76224a8558bSMatt Spinler 
76324a8558bSMatt Spinler         // Add a PEL with the host off
76424a8558bSMatt Spinler         auto pel = makePEL();
76524a8558bSMatt Spinler         repo.add(pel);
76624a8558bSMatt Spinler 
76724a8558bSMatt Spinler         // Not added to the send queue
76824a8558bSMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
76924a8558bSMatt Spinler 
77024a8558bSMatt Spinler         dataIface.changeHostState(true);
77124a8558bSMatt Spinler 
77224a8558bSMatt Spinler         // Try again with the host on
77324a8558bSMatt Spinler         pel = makePEL();
77424a8558bSMatt Spinler         repo.add(pel);
77524a8558bSMatt Spinler 
77624a8558bSMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
77724a8558bSMatt Spinler 
77824a8558bSMatt Spinler         // Now getHostPELEnablement() will return true for the new PEL
77924a8558bSMatt Spinler         pel = makePEL();
78024a8558bSMatt Spinler         repo.add(pel);
78124a8558bSMatt Spinler 
78224a8558bSMatt Spinler         EXPECT_EQ(notifier.queueSize(), 1);
78324a8558bSMatt Spinler     }
78424a8558bSMatt Spinler 
78524a8558bSMatt Spinler     // getHostPELEnablement is back to returning false.
78624a8558bSMatt Spinler     // Create a new second instance and make sure the 3 existing PELs
78724a8558bSMatt Spinler     // aren't put on the queue on startup
78824a8558bSMatt Spinler     {
78924a8558bSMatt Spinler         Repository repo1{repoPath};
79024a8558bSMatt Spinler         std::unique_ptr<HostInterface> hostIface1 =
79124a8558bSMatt Spinler             std::make_unique<MockHostInterface>(event, dataIface);
79224a8558bSMatt Spinler 
79324a8558bSMatt Spinler         HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
79424a8558bSMatt Spinler 
79524a8558bSMatt Spinler         EXPECT_EQ(notifier.queueSize(), 0);
79624a8558bSMatt Spinler     }
79724a8558bSMatt Spinler }
798