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 = [&hostState, 125 &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