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