xref: /openbmc/phosphor-logging/test/openpower-pels/host_notifier_test.cpp (revision 527ff346907354a2ce03678c1c2a523108e8b4bc)
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:
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 = std::make_unique<NiceMock<MockHostInterface>>(event,
51                                                                    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  
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   */
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   */
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
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.
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
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.
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
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.
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
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
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
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
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
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
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
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
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
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
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
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