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