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