xref: /openbmc/phosphor-logging/test/openpower-pels/host_notifier_test.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3 
4 #include "extensions/openpower-pels/data_interface.hpp"
5 #include "extensions/openpower-pels/host_notifier.hpp"
6 #include "mocks.hpp"
7 #include "pel_utils.hpp"
8 
9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 
13 #include <chrono>
14 
15 #include <gtest/gtest.h>
16 
17 using namespace openpower::pels;
18 using ::testing::_;
19 using ::testing::Invoke;
20 using ::testing::NiceMock;
21 using ::testing::Return;
22 namespace fs = std::filesystem;
23 using namespace std::chrono;
24 
25 const size_t actionFlags0Offset = 66;
26 const size_t actionFlags1Offset = 67;
27 
28 class HostNotifierTest : public CleanPELFiles
29 {
30   public:
HostNotifierTest()31     HostNotifierTest() : repo(repoPath)
32     {
33         auto r = sd_event_default(&event);
34         EXPECT_TRUE(r >= 0);
35 
36         ON_CALL(dataIface, getHostPELEnablement).WillByDefault(Return(true));
37 
38         hostIface =
39             std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
40 
41         mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
42 
43         auto send = [this](uint32_t /*id*/, uint32_t /*size*/) {
44             return this->mockHostIface->send(0);
45         };
46 
47         // Unless otherwise specified, sendNewLogCmd should always pass.
48         ON_CALL(*mockHostIface, sendNewLogCmd(_, _)).WillByDefault(send);
49     }
50 
~HostNotifierTest()51     ~HostNotifierTest()
52     {
53         sd_event_unref(event);
54     }
55 
56   protected:
57     sd_event* event;
58     Repository repo;
59     NiceMock<MockDataInterface> dataIface;
60     std::unique_ptr<HostInterface> hostIface;
61     MockHostInterface* mockHostIface;
62 };
63 
64 /**
65  * @brief Create PEL with the specified action flags
66  *
67  * @param[in] actionFlagsMask - Optional action flags to use
68  *
69  * @return std::unique_ptr<PEL>
70  */
makePEL(uint16_t actionFlagsMask=0)71 std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
72 {
73     static uint32_t obmcID = 1;
74     auto data = pelDataFactory(TestPELType::pelSimple);
75 
76     data[actionFlags0Offset] |= actionFlagsMask >> 8;
77     data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
78 
79     auto pel = std::make_unique<PEL>(data, obmcID++);
80     pel->assignID();
81     pel->setCommitTime();
82     return pel;
83 }
84 
85 /**
86  * @brief Run an iteration of the event loop.
87  *
88  * An event loop is used for:
89  *   1) timer expiration callbacks
90  *   2) Dispatches
91  *   3) host interface receive callbacks
92  *
93  * @param[in] event - The event object
94  * @param[in] numEvents - number of times to call Event::run()
95  * @param[in] timeout - timeout value for run()
96  */
runEvents(sdeventplus::Event & event,size_t numEvents,milliseconds timeout=milliseconds (1))97 void runEvents(sdeventplus::Event& event, size_t numEvents,
98                milliseconds timeout = milliseconds(1))
99 {
100     for (size_t i = 0; i < numEvents; i++)
101     {
102         event.run(timeout);
103     }
104 }
105 
106 // Test that host state change callbacks work
TEST_F(HostNotifierTest,TestHostStateChange)107 TEST_F(HostNotifierTest, TestHostStateChange)
108 {
109     bool hostState = false;
110     bool called = false;
111     DataInterfaceBase::HostStateChangeFunc func =
112         [&hostState, &called](bool state) {
113             hostState = state;
114             called = true;
115         };
116 
117     dataIface.subscribeToHostStateChange("test", func);
118 
119     // callback called
120     dataIface.changeHostState(true);
121     EXPECT_TRUE(called);
122     EXPECT_TRUE(hostState);
123 
124     // No change, not called
125     called = false;
126     dataIface.changeHostState(true);
127     EXPECT_FALSE(called);
128 
129     // Called again
130     dataIface.changeHostState(false);
131     EXPECT_FALSE(hostState);
132     EXPECT_TRUE(called);
133 
134     // Shouldn't get called after an unsubscribe
135     dataIface.unsubscribeFromHostStateChange("test");
136 
137     called = false;
138 
139     dataIface.changeHostState(true);
140     EXPECT_FALSE(called);
141 }
142 
143 // Test dealing with how acked PELs are put on the
144 // notification queue.
TEST_F(HostNotifierTest,TestPolicyAckedPEL)145 TEST_F(HostNotifierTest, TestPolicyAckedPEL)
146 {
147     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
148 
149     auto pel = makePEL();
150     repo.add(pel);
151 
152     // This is required
153     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
154     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
155 
156     // Not in the repo
157     EXPECT_FALSE(notifier.enqueueRequired(42));
158     EXPECT_FALSE(notifier.notifyRequired(42));
159 
160     // Now set this PEL to host acked
161     repo.setPELHostTransState(pel->id(), TransmissionState::acked);
162 
163     // Since it's acked, doesn't need to be enqueued or transmitted
164     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
165     EXPECT_FALSE(notifier.notifyRequired(pel->id()));
166 }
167 
168 // Test the 'don't report' PEL flag
TEST_F(HostNotifierTest,TestPolicyDontReport)169 TEST_F(HostNotifierTest, TestPolicyDontReport)
170 {
171     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
172 
173     // dontReportToHostFlagBit
174     auto pel = makePEL(0x1000);
175 
176     // Double check the action flag is still set
177     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
178     EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
179 
180     repo.add(pel);
181 
182     // Don't need to send this to the host
183     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
184 }
185 
186 // Test that hidden PELs need notification when there
187 // is no HMC.
TEST_F(HostNotifierTest,TestPolicyHiddenNoHMC)188 TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
189 {
190     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
191 
192     // hiddenFlagBit
193     auto pel = makePEL(0x4000);
194 
195     // Double check the action flag is still set
196     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
197     EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
198 
199     repo.add(pel);
200 
201     // Still need to enqueue this
202     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
203 
204     // Still need to send it
205     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
206 }
207 
208 // Don't need to enqueue a hidden log already acked by the HMC
TEST_F(HostNotifierTest,TestPolicyHiddenWithHMCAcked)209 TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
210 {
211     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
212 
213     // hiddenFlagBit
214     auto pel = makePEL(0x4000);
215 
216     // Double check the action flag is still set
217     std::bitset<16> actionFlags = pel->userHeader().actionFlags();
218     EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
219 
220     repo.add(pel);
221 
222     // No HMC yet, so required
223     EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
224 
225     repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
226 
227     // Not required anymore
228     EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
229 }
230 
231 // Test that changing the HMC manage status affects
232 // the policy with hidden log notification.
TEST_F(HostNotifierTest,TestPolicyHiddenWithHMCManaged)233 TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
234 {
235     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
236 
237     // hiddenFlagBit
238     auto pel = makePEL(0x4000);
239 
240     repo.add(pel);
241 
242     // The first time, the HMC managed is false
243     EXPECT_TRUE(notifier.notifyRequired(pel->id()));
244 
245     dataIface.setHMCManaged(true);
246 
247     // This time, HMC managed is true so no need to notify
248     EXPECT_FALSE(notifier.notifyRequired(pel->id()));
249 }
250 
251 // Test that PELs are enqueued on startup
TEST_F(HostNotifierTest,TestStartup)252 TEST_F(HostNotifierTest, TestStartup)
253 {
254     // Give the repo 10 PELs to start with
255     for (int i = 0; i < 10; i++)
256     {
257         auto pel = makePEL();
258         repo.add(pel);
259     }
260 
261     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
262 
263     ASSERT_EQ(notifier.queueSize(), 10);
264 
265     // Now add 10 more after the notifier is watching
266     for (int i = 0; i < 10; i++)
267     {
268         auto pel = makePEL();
269         repo.add(pel);
270     }
271 
272     ASSERT_EQ(notifier.queueSize(), 20);
273 }
274 
275 // Test the simple path were PELs get sent to the host
TEST_F(HostNotifierTest,TestSendCmd)276 TEST_F(HostNotifierTest, TestSendCmd)
277 {
278     sdeventplus::Event sdEvent{event};
279 
280     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
281 
282     // Add a PEL with the host off
283     auto pel = makePEL();
284     repo.add(pel);
285 
286     EXPECT_EQ(notifier.queueSize(), 1);
287 
288     dataIface.changeHostState(true);
289 
290     runEvents(sdEvent, 2);
291 
292     // It was sent up
293     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
294     EXPECT_EQ(notifier.queueSize(), 0);
295 
296     // Verify the state was written to the PEL.
297     Repository::LogID id{Repository::LogID::Pel{pel->id()}};
298     auto data = repo.getPELData(id);
299     PEL pelFromRepo{*data};
300     EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
301 
302     // Add a few more PELs.  They will get sent.
303     pel = makePEL();
304     repo.add(pel);
305 
306     // Dispatch it by hitting the event loop (no commands sent yet)
307     // Don't need to test this step discretely in the future
308     runEvents(sdEvent, 1);
309     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
310     EXPECT_EQ(notifier.queueSize(), 0);
311 
312     // Send the command
313     runEvents(sdEvent, 1);
314 
315     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
316     EXPECT_EQ(notifier.queueSize(), 0);
317 
318     pel = makePEL();
319     repo.add(pel);
320 
321     // dispatch and process the command
322     runEvents(sdEvent, 2);
323 
324     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
325     EXPECT_EQ(notifier.queueSize(), 0);
326 }
327 
328 // Test that if the class is created with the host up,
329 // it will send PELs
TEST_F(HostNotifierTest,TestStartAfterHostUp)330 TEST_F(HostNotifierTest, TestStartAfterHostUp)
331 {
332     // Add PELs right away
333     auto pel = makePEL();
334     repo.add(pel);
335     pel = makePEL();
336     repo.add(pel);
337 
338     sdeventplus::Event sdEvent{event};
339 
340     // Create the HostNotifier class with the host already up
341     dataIface.changeHostState(true);
342     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
343 
344     // It should start sending PELs right away
345     runEvents(sdEvent, 3);
346 
347     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
348     EXPECT_EQ(notifier.queueSize(), 0);
349 }
350 
351 // Test that a single failure will cause a retry
TEST_F(HostNotifierTest,TestHostRetry)352 TEST_F(HostNotifierTest, TestHostRetry)
353 {
354     sdeventplus::Event sdEvent{event};
355 
356     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
357 
358     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
359         return this->mockHostIface->send(1);
360     };
361     auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
362         return this->mockHostIface->send(0);
363     };
364 
365     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
366         .WillOnce(sendFailure)
367         .WillOnce(sendSuccess)
368         .WillOnce(sendSuccess);
369 
370     dataIface.changeHostState(true);
371 
372     auto pel = makePEL();
373     repo.add(pel);
374 
375     // Dispatch and handle the command
376     runEvents(sdEvent, 2);
377 
378     // The command failed, so the queue isn't empty
379     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
380     EXPECT_EQ(notifier.queueSize(), 1);
381 
382     // Run the events again to let the timer expire and the
383     // command to be retried, which will be successful.
384     runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
385 
386     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
387     EXPECT_EQ(notifier.queueSize(), 0);
388 
389     // This one should pass with no problems
390     pel = makePEL();
391     repo.add(pel);
392 
393     // Dispatch and handle the command
394     runEvents(sdEvent, 2);
395 
396     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
397     EXPECT_EQ(notifier.queueSize(), 0);
398 }
399 
400 // Test that all commands fail and notifier will give up
TEST_F(HostNotifierTest,TestHardFailure)401 TEST_F(HostNotifierTest, TestHardFailure)
402 {
403     sdeventplus::Event sdEvent{event};
404 
405     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
406 
407     // Every call will fail
408     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
409         return this->mockHostIface->send(1);
410     };
411 
412     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
413         .WillRepeatedly(sendFailure);
414 
415     dataIface.changeHostState(true);
416 
417     auto pel = makePEL();
418     repo.add(pel);
419 
420     // Clock more retries than necessary
421     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
422 
423     // Should have stopped after the 15 Tries
424     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
425     EXPECT_EQ(notifier.queueSize(), 1);
426 
427     // Now add another PEL, and it should start trying again
428     // though it will also eventually give up
429     pel = makePEL();
430     repo.add(pel);
431 
432     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
433 
434     // Tried an additional 15 times
435     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
436     EXPECT_EQ(notifier.queueSize(), 2);
437 }
438 
439 // Test that if the command cannot be started it will give
440 // up but still try again later
TEST_F(HostNotifierTest,TestCannotStartCmd)441 TEST_F(HostNotifierTest, TestCannotStartCmd)
442 {
443     sdeventplus::Event sdEvent{event};
444 
445     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
446 
447     // Make it behave like startCommand() fails.
448     auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
449         this->mockHostIface->cancelCmd();
450         return CmdStatus::failure;
451     };
452 
453     auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
454         return this->mockHostIface->send(0);
455     };
456 
457     // Fails 16 times (1 fail + 15  retries) and
458     // then start working.
459     EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
460         .WillOnce(sendFailure)
461         .WillOnce(sendFailure)
462         .WillOnce(sendFailure)
463         .WillOnce(sendFailure)
464         .WillOnce(sendFailure)
465         .WillOnce(sendFailure)
466         .WillOnce(sendFailure)
467         .WillOnce(sendFailure)
468         .WillOnce(sendFailure)
469         .WillOnce(sendFailure)
470         .WillOnce(sendFailure)
471         .WillOnce(sendFailure)
472         .WillOnce(sendFailure)
473         .WillOnce(sendFailure)
474         .WillOnce(sendFailure)
475         .WillOnce(sendFailure)
476         .WillRepeatedly(sendSuccess);
477 
478     dataIface.changeHostState(true);
479 
480     auto pel = makePEL();
481     repo.add(pel);
482 
483     // Clock more retries than necessary
484     runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
485 
486     // Didn't get far enough for a cmd to be processed
487     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 0);
488     EXPECT_EQ(notifier.queueSize(), 1);
489 
490     // At this point, commands will work again.
491 
492     pel = makePEL();
493     repo.add(pel);
494 
495     // Run the events to send the PELs
496     runEvents(sdEvent, 5, mockHostIface->getReceiveRetryDelay());
497 
498     // All PELs sent
499     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
500     EXPECT_EQ(notifier.queueSize(), 0);
501 }
502 
503 // Cancel an in progress command
TEST_F(HostNotifierTest,TestCancelCmd)504 TEST_F(HostNotifierTest, TestCancelCmd)
505 {
506     sdeventplus::Event sdEvent{event};
507     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
508 
509     dataIface.changeHostState(true);
510 
511     // Add and send one PEL, but don't enter the event loop
512     // so the receive function can't run.
513     auto pel = makePEL();
514     repo.add(pel);
515 
516     // Not dispatched yet
517     EXPECT_EQ(notifier.queueSize(), 1);
518 
519     // Dispatch it
520     runEvents(sdEvent, 2);
521 
522     // It was sent and off the queue
523     EXPECT_EQ(notifier.queueSize(), 0);
524 
525     // This will cancel the receive
526     dataIface.changeHostState(false);
527 
528     // Back on the queue
529     EXPECT_EQ(notifier.queueSize(), 1);
530 
531     // Turn the host back on and make sure
532     // commands will work again
533     dataIface.changeHostState(true);
534 
535     runEvents(sdEvent, 1);
536 
537     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
538     EXPECT_EQ(notifier.queueSize(), 0);
539 }
540 
541 // Test that acking a PEL persist across power cycles
TEST_F(HostNotifierTest,TestPowerCycleAndAcks)542 TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
543 {
544     sdeventplus::Event sdEvent{event};
545 
546     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
547 
548     // Add 2 PELs with host off
549     auto pel = makePEL();
550     repo.add(pel);
551     auto id1 = pel->id();
552 
553     pel = makePEL();
554     repo.add(pel);
555     auto id2 = pel->id();
556 
557     dataIface.changeHostState(true);
558 
559     runEvents(sdEvent, 3);
560 
561     // The were both sent.
562     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
563     EXPECT_EQ(notifier.queueSize(), 0);
564 
565     dataIface.changeHostState(false);
566 
567     // Those PELs weren't acked, so they will get sent again
568     EXPECT_EQ(notifier.queueSize(), 2);
569 
570     // Power back on and send them again
571     dataIface.changeHostState(true);
572     runEvents(sdEvent, 3);
573 
574     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
575     EXPECT_EQ(notifier.queueSize(), 0);
576 
577     // Ack them and verify the state in the PEL.
578     notifier.ackPEL(id1);
579     notifier.ackPEL(id2);
580 
581     Repository::LogID id{Repository::LogID::Pel{id1}};
582     auto data = repo.getPELData(id);
583     PEL pelFromRepo1{*data};
584     EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
585 
586     id.pelID.id = id2;
587     data = repo.getPELData(id);
588     PEL pelFromRepo2{*data};
589     EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
590 
591     // Power back off, and they should't get re-added
592     dataIface.changeHostState(false);
593 
594     EXPECT_EQ(notifier.queueSize(), 0);
595 }
596 
597 // Test the host full condition
TEST_F(HostNotifierTest,TestHostFull)598 TEST_F(HostNotifierTest, TestHostFull)
599 {
600     // The full interaction with the host is:
601     // BMC:  new PEL available
602     // Host: ReadPELFile  (not modeled here)
603     // Host: Ack(id) (if not full), or HostFull(id)
604     // BMC: if full and any new PELs come in, don't sent them
605     // Start a timer and try again
606     // Host responds with either Ack or full
607     // and repeat
608 
609     sdeventplus::Event sdEvent{event};
610     HostNotifier notifier{repo, dataIface, std::move(hostIface)};
611 
612     dataIface.changeHostState(true);
613 
614     // Add and dispatch/send one PEL
615     auto pel = makePEL();
616     auto id = pel->id();
617     repo.add(pel);
618     runEvents(sdEvent, 2);
619 
620     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
621     EXPECT_EQ(notifier.queueSize(), 0);
622 
623     // Host is full
624     notifier.setHostFull(id);
625 
626     // It goes back on the queue
627     EXPECT_EQ(notifier.queueSize(), 1);
628 
629     // The transmission state goes back to new
630     Repository::LogID i{Repository::LogID::Pel{id}};
631     auto data = repo.getPELData(i);
632     PEL pelFromRepo{*data};
633     EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
634 
635     // Clock it, nothing should be sent still.
636     runEvents(sdEvent, 1);
637 
638     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
639     EXPECT_EQ(notifier.queueSize(), 1);
640 
641     // Add another PEL and clock it, still nothing sent
642     pel = makePEL();
643     repo.add(pel);
644     runEvents(sdEvent, 2);
645     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
646     EXPECT_EQ(notifier.queueSize(), 2);
647 
648     // Let the host full timer expire to trigger a retry.
649     // Add some extra event passes just to be sure nothing new is sent.
650     runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
651 
652     // The timer expiration will send just the 1, not both
653     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
654     EXPECT_EQ(notifier.queueSize(), 1);
655 
656     // Host still full
657     notifier.setHostFull(id);
658 
659     // Let the host full timer attempt again
660     runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
661     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
662 
663     // Add yet another PEL with the retry timer expired.
664     // It shouldn't get sent out.
665     pel = makePEL();
666     repo.add(pel);
667     runEvents(sdEvent, 2);
668     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
669 
670     // The last 2 PELs still on the queue
671     EXPECT_EQ(notifier.queueSize(), 2);
672 
673     // Host no longer full, it finally acks the first PEL
674     notifier.ackPEL(id);
675 
676     // Now the remaining 2 PELs will be dispatched
677     runEvents(sdEvent, 3);
678 
679     EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
680     EXPECT_EQ(notifier.queueSize(), 0);
681 }
682 
683 // Test when the host says it was send a malformed PEL
TEST_F(HostNotifierTest,TestBadPEL)684 TEST_F(HostNotifierTest, TestBadPEL)
685 {
686     sdeventplus::Event sdEvent{event};
687 
688     {
689         Repository repo1{repoPath};
690         HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
691 
692         dataIface.changeHostState(true);
693 
694         // Add a PEL and dispatch and send it
695         auto pel = makePEL();
696         auto id = pel->id();
697         repo1.add(pel);
698 
699         runEvents(sdEvent, 2);
700         EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
701         EXPECT_EQ(notifier.queueSize(), 0);
702 
703         // The host rejected it.
704         notifier.setBadPEL(id);
705 
706         // Doesn't go back on the queue
707         EXPECT_EQ(notifier.queueSize(), 0);
708 
709         // Check the state was saved in the PEL itself
710         Repository::LogID i{Repository::LogID::Pel{id}};
711         auto data = repo1.getPELData(i);
712         PEL pelFromRepo{*data};
713         EXPECT_EQ(pelFromRepo.hostTransmissionState(),
714                   TransmissionState::badPEL);
715 
716         dataIface.changeHostState(false);
717 
718         // Ensure it doesn't go back on the queue on a power cycle
719         EXPECT_EQ(notifier.queueSize(), 0);
720     }
721 
722     // Now restore the repo, and make sure it doesn't come back
723     {
724         Repository repo1{repoPath};
725 
726         std::unique_ptr<HostInterface> hostIface1 =
727             std::make_unique<MockHostInterface>(event, dataIface);
728 
729         HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
730 
731         EXPECT_EQ(notifier.queueSize(), 0);
732     }
733 }
734 
735 // Test that sending PELs can be disabled
TEST_F(HostNotifierTest,TestDisable)736 TEST_F(HostNotifierTest, TestDisable)
737 {
738     // Turn off sending the PELs except for once in the middle
739     EXPECT_CALL(dataIface, getHostPELEnablement())
740         .WillOnce(Return(false))
741         .WillOnce(Return(false))
742         .WillOnce(Return(true))
743         .WillOnce(Return(false))
744         .WillOnce(Return(false))
745         .WillOnce(Return(false));
746 
747     {
748         HostNotifier notifier{repo, dataIface, std::move(hostIface)};
749 
750         // Add a PEL with the host off
751         auto pel = makePEL();
752         repo.add(pel);
753 
754         // Not added to the send queue
755         EXPECT_EQ(notifier.queueSize(), 0);
756 
757         dataIface.changeHostState(true);
758 
759         // Try again with the host on
760         pel = makePEL();
761         repo.add(pel);
762 
763         EXPECT_EQ(notifier.queueSize(), 0);
764 
765         // Now getHostPELEnablement() will return true for the new PEL
766         pel = makePEL();
767         repo.add(pel);
768 
769         EXPECT_EQ(notifier.queueSize(), 1);
770     }
771 
772     // getHostPELEnablement is back to returning false.
773     // Create a new second instance and make sure the 3 existing PELs
774     // aren't put on the queue on startup
775     {
776         Repository repo1{repoPath};
777         std::unique_ptr<HostInterface> hostIface1 =
778             std::make_unique<MockHostInterface>(event, dataIface);
779 
780         HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
781 
782         EXPECT_EQ(notifier.queueSize(), 0);
783     }
784 }
785