1 /** 2 * Copyright © 2021 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 17 #include "action.hpp" 18 #include "chassis.hpp" 19 #include "device.hpp" 20 #include "i2c_capture_bytes_action.hpp" 21 #include "i2c_compare_bit_action.hpp" 22 #include "i2c_interface.hpp" 23 #include "if_action.hpp" 24 #include "log_phase_fault_action.hpp" 25 #include "mock_action.hpp" 26 #include "mock_error_logging.hpp" 27 #include "mock_journal.hpp" 28 #include "mock_services.hpp" 29 #include "mocked_i2c_interface.hpp" 30 #include "phase_fault.hpp" 31 #include "phase_fault_detection.hpp" 32 #include "rule.hpp" 33 #include "system.hpp" 34 35 #include <cstdint> 36 #include <map> 37 #include <memory> 38 #include <stdexcept> 39 #include <string> 40 #include <utility> 41 #include <vector> 42 43 #include <gmock/gmock.h> 44 #include <gtest/gtest.h> 45 46 using namespace phosphor::power::regulators; 47 48 using ::testing::_; 49 using ::testing::A; 50 using ::testing::NotNull; 51 using ::testing::Ref; 52 using ::testing::Return; 53 using ::testing::SetArrayArgument; 54 using ::testing::Throw; 55 using ::testing::TypedEq; 56 57 class PhaseFaultDetectionTests : public ::testing::Test 58 { 59 public: 60 /** 61 * Constructor. 62 * 63 * Creates the following objects needed for calling the 64 * PhaseFaultDetection::execute() method: 65 * - Regulator Device 66 * - I/O expander Device 67 * - Chassis that contains the Devices 68 * - System that contains the Chassis 69 * 70 * Saves pointers to these objects in data members so they can be easily 71 * accessed in tests. Also saves pointers to the MockI2CInterface objects 72 * so they can be used in mock expectations. 73 */ 74 PhaseFaultDetectionTests() : ::testing::Test{} 75 { 76 // Create mock I2CInterface for regulator Device and save pointer 77 auto regI2CInterface = std::make_unique<i2c::MockedI2CInterface>(); 78 this->regI2CInterface = regI2CInterface.get(); 79 80 // Create regulator Device and save pointer 81 auto regulator = std::make_unique<Device>( 82 "vdd1", true, 83 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1", 84 std::move(regI2CInterface)); 85 this->regulator = regulator.get(); 86 87 // Create mock I2CInterface for I/O expander Device and save pointer 88 auto ioExpI2CInterface = std::make_unique<i2c::MockedI2CInterface>(); 89 this->ioExpI2CInterface = ioExpI2CInterface.get(); 90 91 // Create I/O expander Device and save pointer 92 auto ioExpander = std::make_unique<Device>( 93 "ioexp1", false, 94 "/xyz/openbmc_project/inventory/system/chassis/motherboard/ioexp1", 95 std::move(ioExpI2CInterface)); 96 this->ioExpander = ioExpander.get(); 97 98 // Create Chassis that contains Devices and save pointer 99 std::vector<std::unique_ptr<Device>> devices{}; 100 devices.emplace_back(std::move(regulator)); 101 devices.emplace_back(std::move(ioExpander)); 102 auto chassis = std::make_unique<Chassis>( 103 1, "/xyz/openbmc_project/inventory/system/chassis", 104 std::move(devices)); 105 this->chassis = chassis.get(); 106 107 // Create System that contains Chassis and save pointer 108 std::vector<std::unique_ptr<Rule>> rules{}; 109 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 110 chassisVec.emplace_back(std::move(chassis)); 111 this->system = 112 std::make_unique<System>(std::move(rules), std::move(chassisVec)); 113 } 114 115 protected: 116 /** 117 * Note: The following pointers do NOT need to be explicitly deleted. They 118 * point to objects that are owned by the System object. All the objects 119 * will be automatically deleted. 120 */ 121 i2c::MockedI2CInterface* regI2CInterface{nullptr}; 122 Device* regulator{nullptr}; 123 i2c::MockedI2CInterface* ioExpI2CInterface{nullptr}; 124 Device* ioExpander{nullptr}; 125 Chassis* chassis{nullptr}; 126 127 /** 128 * System object. Owns all the other objects and will automatically delete 129 * them. 130 */ 131 std::unique_ptr<System> system{}; 132 }; 133 134 TEST_F(PhaseFaultDetectionTests, Constructor) 135 { 136 // Test where device ID not specified 137 { 138 std::vector<std::unique_ptr<Action>> actions{}; 139 actions.push_back(std::make_unique<MockAction>()); 140 141 PhaseFaultDetection detection{std::move(actions)}; 142 EXPECT_EQ(detection.getActions().size(), 1); 143 EXPECT_EQ(detection.getDeviceID(), ""); 144 } 145 146 // Test where device ID not specified 147 { 148 std::vector<std::unique_ptr<Action>> actions{}; 149 actions.push_back(std::make_unique<MockAction>()); 150 actions.push_back(std::make_unique<MockAction>()); 151 152 PhaseFaultDetection detection{std::move(actions), "ioexp1"}; 153 EXPECT_EQ(detection.getActions().size(), 2); 154 EXPECT_EQ(detection.getDeviceID(), "ioexp1"); 155 } 156 } 157 158 TEST_F(PhaseFaultDetectionTests, ClearErrorHistory) 159 { 160 std::vector<std::unique_ptr<Action>> actions{}; 161 162 // Create MockAction that will switch every 5 times between working and 163 // throwing an exception. Expect it to be executed 20 times. 164 std::logic_error error{"Logic error"}; 165 auto action = std::make_unique<MockAction>(); 166 EXPECT_CALL(*action, execute) 167 .Times(20) 168 .WillOnce(Return(true)) 169 .WillOnce(Return(true)) 170 .WillOnce(Return(true)) 171 .WillOnce(Return(true)) 172 .WillOnce(Return(true)) 173 .WillOnce(Throw(error)) 174 .WillOnce(Throw(error)) 175 .WillOnce(Throw(error)) 176 .WillOnce(Throw(error)) 177 .WillOnce(Throw(error)) 178 .WillOnce(Return(true)) 179 .WillOnce(Return(true)) 180 .WillOnce(Return(true)) 181 .WillOnce(Return(true)) 182 .WillOnce(Return(true)) 183 .WillOnce(Throw(error)) 184 .WillOnce(Throw(error)) 185 .WillOnce(Throw(error)) 186 .WillOnce(Throw(error)) 187 .WillOnce(Throw(error)); 188 actions.push_back(std::move(action)); 189 190 // Create a LogPhaseFaultAction that will log N faults 191 actions.push_back(std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n)); 192 193 // Create a LogPhaseFaultAction that will log N+1 faults 194 actions.push_back( 195 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1)); 196 197 // Create PhaseFaultDetection 198 PhaseFaultDetection detection{std::move(actions)}; 199 200 // Create lambda that sets Journal and ErrorLogging expectations when 201 // performing phase fault detection 10 times. The lambda allows us to 202 // set the same expectations twice without duplicate code. 203 auto setExpectations = [](MockServices& services) { 204 // Set Journal service expectations: 205 // - 3 error messages for the MockAction exceptions 206 // - 3 error messages for inability to detect phase faults 207 // - 2 error messages for the N phase fault 208 // - 2 error messages for the N+1 phase fault 209 MockJournal& journal = services.getMockJournal(); 210 EXPECT_CALL(journal, logError(std::vector<std::string>{"Logic error"})) 211 .Times(3); 212 EXPECT_CALL(journal, 213 logError("Unable to detect phase faults in regulator vdd1")) 214 .Times(3); 215 EXPECT_CALL( 216 journal, 217 logError("n phase fault detected in regulator vdd1: count=1")) 218 .Times(1); 219 EXPECT_CALL( 220 journal, 221 logError("n phase fault detected in regulator vdd1: count=2")) 222 .Times(1); 223 EXPECT_CALL( 224 journal, 225 logError("n+1 phase fault detected in regulator vdd1: count=1")) 226 .Times(1); 227 EXPECT_CALL( 228 journal, 229 logError("n+1 phase fault detected in regulator vdd1: count=2")) 230 .Times(1); 231 232 // Set ErrorLogging service expectations: 233 // - Internal error should be logged once for the MockAction exceptions 234 // - N phase fault error should be logged once 235 // - N+1 phase fault error should be logged once 236 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 237 EXPECT_CALL(errorLogging, logInternalError).Times(1); 238 EXPECT_CALL(errorLogging, logPhaseFault(_, _, PhaseFaultType::n, _, _)) 239 .Times(1); 240 EXPECT_CALL(errorLogging, 241 logPhaseFault(_, _, PhaseFaultType::n_plus_1, _, _)) 242 .Times(1); 243 }; 244 245 // Perform phase fault detection 10 times to set error history data members 246 { 247 // Create mock services. Set expectations via lambda. 248 MockServices services{}; 249 setExpectations(services); 250 251 // Execute PhaseFaultDetection 10 times 252 for (int i = 1; i <= 10; ++i) 253 { 254 detection.execute(services, *system, *chassis, *regulator); 255 } 256 } 257 258 // Clear error history 259 detection.clearErrorHistory(); 260 261 // Perform phase fault detection 10 more times. Verify errors logged again. 262 { 263 // Create mock services. Set expectations via lambda. 264 MockServices services{}; 265 setExpectations(services); 266 267 // Execute PhaseFaultDetection 10 times 268 for (int i = 1; i <= 10; ++i) 269 { 270 detection.execute(services, *system, *chassis, *regulator); 271 } 272 } 273 } 274 275 TEST_F(PhaseFaultDetectionTests, Execute) 276 { 277 // Test where Device ID was specified 278 { 279 // Create I2CCompareBitAction that will use an I2CInterface 280 auto action = std::make_unique<I2CCompareBitAction>(0x1C, 2, 0); 281 282 // Create PhaseFaultDetection. Specify device ID of I/O expander. 283 std::vector<std::unique_ptr<Action>> actions{}; 284 actions.push_back(std::move(action)); 285 PhaseFaultDetection detection{std::move(actions), "ioexp1"}; 286 287 // Set expectations for regulator I2C interface. Should not be used. 288 EXPECT_CALL(*regI2CInterface, isOpen).Times(0); 289 EXPECT_CALL(*regI2CInterface, read(0x1C, A<uint8_t&>())).Times(0); 290 291 // Set expectations for I/O expander I2C interface. Should be used. 292 EXPECT_CALL(*ioExpI2CInterface, isOpen).Times(1).WillOnce(Return(true)); 293 EXPECT_CALL(*ioExpI2CInterface, read(0x1C, A<uint8_t&>())).Times(1); 294 295 // Create mock services. Expect no errors to be logged. 296 MockServices services{}; 297 MockJournal& journal = services.getMockJournal(); 298 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 299 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 300 EXPECT_CALL(errorLogging, logPhaseFault).Times(0); 301 302 // Execute PhaseFaultDetection 303 detection.execute(services, *system, *chassis, *regulator); 304 } 305 306 // Test where Device ID was not specified 307 { 308 // Create I2CCompareBitAction that will use an I2CInterface 309 auto action = std::make_unique<I2CCompareBitAction>(0x1C, 2, 0); 310 311 // Create PhaseFaultDetection. Specify no device ID, which means the 312 // regulator should be used. 313 std::vector<std::unique_ptr<Action>> actions{}; 314 actions.push_back(std::move(action)); 315 PhaseFaultDetection detection{std::move(actions)}; 316 317 // Set expectations for regulator I2C interface. Should be used. 318 EXPECT_CALL(*regI2CInterface, isOpen).Times(1).WillOnce(Return(true)); 319 EXPECT_CALL(*regI2CInterface, read(0x1C, A<uint8_t&>())).Times(1); 320 321 // Set expectations for I/O expander I2C interface. Should not be used. 322 EXPECT_CALL(*ioExpI2CInterface, isOpen).Times(0); 323 EXPECT_CALL(*ioExpI2CInterface, read(0x1C, A<uint8_t&>())).Times(0); 324 325 // Create mock services. Expect no errors to be logged. 326 MockServices services{}; 327 MockJournal& journal = services.getMockJournal(); 328 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 329 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 330 EXPECT_CALL(errorLogging, logPhaseFault).Times(0); 331 332 // Execute PhaseFaultDetection 333 detection.execute(services, *system, *chassis, *regulator); 334 } 335 336 // Test where no phase faults detected 337 { 338 // Create MockAction. Expect it to be executed 3 times. 339 auto action = std::make_unique<MockAction>(); 340 EXPECT_CALL(*action, execute).Times(3).WillRepeatedly(Return(true)); 341 342 // Create PhaseFaultDetection 343 std::vector<std::unique_ptr<Action>> actions{}; 344 actions.push_back(std::move(action)); 345 PhaseFaultDetection detection{std::move(actions)}; 346 347 // Create mock services. Expect no errors to be logged. 348 MockServices services{}; 349 MockJournal& journal = services.getMockJournal(); 350 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 351 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 352 EXPECT_CALL(errorLogging, logPhaseFault).Times(0); 353 354 // Execute PhaseFaultDetection 3 times 355 for (int i = 1; i <= 3; ++i) 356 { 357 detection.execute(services, *system, *chassis, *regulator); 358 } 359 } 360 361 // Test where N fault occurs, but not twice in a row 362 { 363 // Create MockAction that will alternate between returning true and 364 // false. Expect it to be executed 6 times. Use it for the "condition" 365 // of an IfAction. 366 auto conditionAction = std::make_unique<MockAction>(); 367 EXPECT_CALL(*conditionAction, execute) 368 .Times(6) 369 .WillOnce(Return(true)) 370 .WillOnce(Return(false)) 371 .WillOnce(Return(true)) 372 .WillOnce(Return(false)) 373 .WillOnce(Return(true)) 374 .WillOnce(Return(false)); 375 376 // Create a LogPhaseFaultAction that will log an N phase fault in the 377 // ActionEnvironment. Use it for the "then" clause of an IfAction. 378 auto logPhaseFaultAction = 379 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n); 380 381 // Create an IfAction that will log an N phase fault in the 382 // ActionEnvironment if the mock condition is true. 383 std::vector<std::unique_ptr<Action>> thenActions{}; 384 thenActions.push_back(std::move(logPhaseFaultAction)); 385 auto ifAction = std::make_unique<IfAction>(std::move(conditionAction), 386 std::move(thenActions)); 387 388 // Create PhaseFaultDetection 389 std::vector<std::unique_ptr<Action>> actions{}; 390 actions.push_back(std::move(ifAction)); 391 PhaseFaultDetection detection{std::move(actions)}; 392 393 // Create mock services. Expect 3 error messages in the journal for 394 // an N phase fault detected with the consecutive count = 1. Expect no 395 // phase fault error to be logged. 396 MockServices services{}; 397 MockJournal& journal = services.getMockJournal(); 398 EXPECT_CALL( 399 journal, 400 logError("n phase fault detected in regulator vdd1: count=1")) 401 .Times(3); 402 EXPECT_CALL( 403 journal, 404 logError("n phase fault detected in regulator vdd1: count=2")) 405 .Times(0); 406 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 407 EXPECT_CALL(errorLogging, logPhaseFault).Times(0); 408 409 // Execute PhaseFaultDetection 6 times 410 for (int i = 1; i <= 6; ++i) 411 { 412 detection.execute(services, *system, *chassis, *regulator); 413 } 414 } 415 416 // Test where N+1 fault occurs, but not twice in a row 417 { 418 // Create MockAction that will alternate between returning true and 419 // false. Expect it to be executed 6 times. Use it for the "condition" 420 // of an IfAction. 421 auto conditionAction = std::make_unique<MockAction>(); 422 EXPECT_CALL(*conditionAction, execute) 423 .Times(6) 424 .WillOnce(Return(true)) 425 .WillOnce(Return(false)) 426 .WillOnce(Return(true)) 427 .WillOnce(Return(false)) 428 .WillOnce(Return(true)) 429 .WillOnce(Return(false)); 430 431 // Create a LogPhaseFaultAction that will log an N+1 phase fault in the 432 // ActionEnvironment. Use it for the "then" clause of an IfAction. 433 auto logPhaseFaultAction = 434 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1); 435 436 // Create an IfAction that will log an N+1 phase fault in the 437 // ActionEnvironment if the mock condition is true. 438 std::vector<std::unique_ptr<Action>> thenActions{}; 439 thenActions.push_back(std::move(logPhaseFaultAction)); 440 auto ifAction = std::make_unique<IfAction>(std::move(conditionAction), 441 std::move(thenActions)); 442 443 // Create PhaseFaultDetection 444 std::vector<std::unique_ptr<Action>> actions{}; 445 actions.push_back(std::move(ifAction)); 446 PhaseFaultDetection detection{std::move(actions)}; 447 448 // Create mock services. Expect 3 error messages in the journal for 449 // an N+1 phase fault detected with the consecutive count = 1. Expect 450 // no phase fault error to be logged. 451 MockServices services{}; 452 MockJournal& journal = services.getMockJournal(); 453 EXPECT_CALL( 454 journal, 455 logError("n+1 phase fault detected in regulator vdd1: count=1")) 456 .Times(3); 457 EXPECT_CALL( 458 journal, 459 logError("n+1 phase fault detected in regulator vdd1: count=2")) 460 .Times(0); 461 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 462 EXPECT_CALL(errorLogging, logPhaseFault).Times(0); 463 464 // Execute PhaseFaultDetection 6 times 465 for (int i = 1; i <= 6; ++i) 466 { 467 detection.execute(services, *system, *chassis, *regulator); 468 } 469 } 470 471 // Test where N fault detected twice in a row 472 { 473 // Create action that will log an N phase fault in ActionEnvironment 474 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n); 475 476 // Create PhaseFaultDetection 477 std::vector<std::unique_ptr<Action>> actions{}; 478 actions.push_back(std::move(action)); 479 PhaseFaultDetection detection{std::move(actions)}; 480 481 // Create mock services with the following expectations: 482 // - 2 error messages in journal for N phase fault detected 483 // - 0 error messages in journal for N+1 phase fault detected 484 // - 1 N phase fault error logged 485 // - 0 N+1 phase fault errors logged 486 MockServices services{}; 487 MockJournal& journal = services.getMockJournal(); 488 EXPECT_CALL( 489 journal, 490 logError("n phase fault detected in regulator vdd1: count=1")) 491 .Times(1); 492 EXPECT_CALL( 493 journal, 494 logError("n phase fault detected in regulator vdd1: count=2")) 495 .Times(1); 496 EXPECT_CALL( 497 journal, 498 logError("n+1 phase fault detected in regulator vdd1: count=1")) 499 .Times(0); 500 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 501 std::map<std::string, std::string> additionalData{}; 502 EXPECT_CALL(errorLogging, 503 logPhaseFault(Entry::Level::Warning, Ref(journal), 504 PhaseFaultType::n, regulator->getFRU(), 505 additionalData)) 506 .Times(1); 507 EXPECT_CALL(errorLogging, 508 logPhaseFault(_, _, PhaseFaultType::n_plus_1, _, _)) 509 .Times(0); 510 511 // Execute PhaseFaultDetection 5 times 512 for (int i = 1; i <= 5; ++i) 513 { 514 detection.execute(services, *system, *chassis, *regulator); 515 } 516 } 517 518 // Test where N+1 fault detected twice in a row 519 { 520 // Create action that will log an N+1 phase fault in ActionEnvironment 521 auto action = 522 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1); 523 524 // Create PhaseFaultDetection 525 std::vector<std::unique_ptr<Action>> actions{}; 526 actions.push_back(std::move(action)); 527 PhaseFaultDetection detection{std::move(actions)}; 528 529 // Create mock services with the following expectations: 530 // - 2 error messages in journal for N+1 phase fault detected 531 // - 0 error messages in journal for N phase fault detected 532 // - 1 N+1 phase fault error logged 533 // - 0 N phase fault errors logged 534 MockServices services{}; 535 MockJournal& journal = services.getMockJournal(); 536 EXPECT_CALL( 537 journal, 538 logError("n+1 phase fault detected in regulator vdd1: count=1")) 539 .Times(1); 540 EXPECT_CALL( 541 journal, 542 logError("n+1 phase fault detected in regulator vdd1: count=2")) 543 .Times(1); 544 EXPECT_CALL( 545 journal, 546 logError("n phase fault detected in regulator vdd1: count=1")) 547 .Times(0); 548 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 549 std::map<std::string, std::string> additionalData{}; 550 EXPECT_CALL(errorLogging, 551 logPhaseFault(Entry::Level::Informational, Ref(journal), 552 PhaseFaultType::n_plus_1, regulator->getFRU(), 553 additionalData)) 554 .Times(1); 555 EXPECT_CALL(errorLogging, logPhaseFault(_, _, PhaseFaultType::n, _, _)) 556 .Times(0); 557 558 // Execute PhaseFaultDetection 5 times 559 for (int i = 1; i <= 5; ++i) 560 { 561 detection.execute(services, *system, *chassis, *regulator); 562 } 563 } 564 565 // Test where both faults detected twice in a row 566 { 567 std::vector<std::unique_ptr<Action>> actions{}; 568 569 // Create action that will log an N+1 phase fault in ActionEnvironment 570 actions.push_back( 571 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1)); 572 573 // Create action that will log an N phase fault in ActionEnvironment 574 actions.push_back( 575 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n)); 576 577 // Create PhaseFaultDetection 578 PhaseFaultDetection detection{std::move(actions)}; 579 580 // Create mock services with the following expectations: 581 // - 2 error messages in journal for N+1 phase fault detected 582 // - 2 error messages in journal for N phase fault detected 583 // - 1 N+1 phase fault error logged 584 // - 1 N phase fault error logged 585 MockServices services{}; 586 MockJournal& journal = services.getMockJournal(); 587 EXPECT_CALL( 588 journal, 589 logError("n+1 phase fault detected in regulator vdd1: count=1")) 590 .Times(1); 591 EXPECT_CALL( 592 journal, 593 logError("n+1 phase fault detected in regulator vdd1: count=2")) 594 .Times(1); 595 EXPECT_CALL( 596 journal, 597 logError("n phase fault detected in regulator vdd1: count=1")) 598 .Times(1); 599 EXPECT_CALL( 600 journal, 601 logError("n phase fault detected in regulator vdd1: count=2")) 602 .Times(1); 603 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 604 std::map<std::string, std::string> additionalData{}; 605 EXPECT_CALL(errorLogging, 606 logPhaseFault(Entry::Level::Informational, Ref(journal), 607 PhaseFaultType::n_plus_1, regulator->getFRU(), 608 additionalData)) 609 .Times(1); 610 EXPECT_CALL(errorLogging, 611 logPhaseFault(Entry::Level::Warning, Ref(journal), 612 PhaseFaultType::n, regulator->getFRU(), 613 additionalData)) 614 .Times(1); 615 616 // Execute PhaseFaultDetection 5 times 617 for (int i = 1; i <= 5; ++i) 618 { 619 detection.execute(services, *system, *chassis, *regulator); 620 } 621 } 622 623 // Test where additional error data is captured 624 { 625 std::vector<std::unique_ptr<Action>> actions{}; 626 627 // Create action that will capture 1 byte from register 0x0F 628 actions.push_back(std::make_unique<I2CCaptureBytesAction>(0x0F, 1)); 629 630 // Create action that will capture 2 bytes from register 0x21 631 actions.push_back(std::make_unique<I2CCaptureBytesAction>(0x21, 2)); 632 633 // Create action that will log an N phase fault in ActionEnvironment 634 actions.push_back( 635 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n)); 636 637 // Create PhaseFaultDetection 638 PhaseFaultDetection detection{std::move(actions)}; 639 640 // Set expectations for regulator I2C interface: 641 // - isOpen() will return true 642 // - reading 1 byte from register 0x0F will return 0xDA 643 // - reading 2 bytes from register 0x21 will return [ 0x56, 0x14 ] 644 EXPECT_CALL(*regI2CInterface, isOpen).WillRepeatedly(Return(true)); 645 uint8_t register0FValues[] = {0xDA}; 646 EXPECT_CALL(*regI2CInterface, 647 read(0x0F, TypedEq<uint8_t&>(1), NotNull(), 648 i2c::I2CInterface::Mode::I2C)) 649 .Times(5) 650 .WillRepeatedly( 651 SetArrayArgument<2>(register0FValues, register0FValues + 1)); 652 uint8_t register21Values[] = {0x56, 0x14}; 653 EXPECT_CALL(*regI2CInterface, 654 read(0x21, TypedEq<uint8_t&>(2), NotNull(), 655 i2c::I2CInterface::Mode::I2C)) 656 .Times(5) 657 .WillRepeatedly( 658 SetArrayArgument<2>(register21Values, register21Values + 2)); 659 660 // Create mock services with the following expectations: 661 // - 2 error messages in journal for N phase fault detected 662 // - 1 N phase fault error logged with additional data 663 MockServices services{}; 664 MockJournal& journal = services.getMockJournal(); 665 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(2); 666 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 667 std::map<std::string, std::string> additionalData{ 668 {"vdd1_register_0xF", "[ 0xDA ]"}, 669 {"vdd1_register_0x21", "[ 0x56, 0x14 ]"}}; 670 EXPECT_CALL(errorLogging, 671 logPhaseFault(Entry::Level::Warning, Ref(journal), 672 PhaseFaultType::n, regulator->getFRU(), 673 additionalData)) 674 .Times(1); 675 676 // Execute PhaseFaultDetection 5 times 677 for (int i = 1; i <= 5; ++i) 678 { 679 detection.execute(services, *system, *chassis, *regulator); 680 } 681 } 682 683 // Test where fails: Exception thrown by actions 684 { 685 // Create I2CCompareBitAction that will use the I2CInterface 686 auto action = std::make_unique<I2CCompareBitAction>(0x7C, 2, 0); 687 688 // Create PhaseFaultDetection 689 std::vector<std::unique_ptr<Action>> actions{}; 690 actions.push_back(std::move(action)); 691 PhaseFaultDetection detection{std::move(actions)}; 692 693 // Set expectations for regulator I2C interface: 694 // - isOpen() will return true 695 // - reading 1 byte from register 0x7C will throw an I2CException 696 EXPECT_CALL(*regI2CInterface, isOpen).WillRepeatedly(Return(true)); 697 EXPECT_CALL(*regI2CInterface, read(0x7C, A<uint8_t&>())) 698 .Times(5) 699 .WillRepeatedly(Throw( 700 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70})); 701 702 // Create mock services with the following expectations: 703 // - 3 error messages in journal for exception 704 // - 3 error messages in journal for inability to detect phase faults 705 // - 1 I2C error logged 706 MockServices services{}; 707 MockJournal& journal = services.getMockJournal(); 708 std::vector<std::string> exceptionMessages{ 709 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70", 710 "ActionError: i2c_compare_bit: { register: 0x7C, position: 2, " 711 "value: 0 }"}; 712 EXPECT_CALL(journal, logError(exceptionMessages)).Times(3); 713 EXPECT_CALL(journal, 714 logError("Unable to detect phase faults in regulator vdd1")) 715 .Times(3); 716 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 717 EXPECT_CALL(errorLogging, 718 logI2CError(Entry::Level::Warning, Ref(journal), 719 "/dev/i2c-1", 0x70, 0)) 720 .Times(1); 721 722 // Execute PhaseFaultDetection 5 times 723 for (int i = 1; i <= 5; ++i) 724 { 725 detection.execute(services, *system, *chassis, *regulator); 726 } 727 } 728 } 729 730 TEST_F(PhaseFaultDetectionTests, GetActions) 731 { 732 std::vector<std::unique_ptr<Action>> actions{}; 733 734 MockAction* action1 = new MockAction{}; 735 actions.push_back(std::unique_ptr<MockAction>{action1}); 736 737 MockAction* action2 = new MockAction{}; 738 actions.push_back(std::unique_ptr<MockAction>{action2}); 739 740 PhaseFaultDetection detection{std::move(actions)}; 741 EXPECT_EQ(detection.getActions().size(), 2); 742 EXPECT_EQ(detection.getActions()[0].get(), action1); 743 EXPECT_EQ(detection.getActions()[1].get(), action2); 744 } 745 746 TEST_F(PhaseFaultDetectionTests, GetDeviceID) 747 { 748 // Test where device ID not specified 749 { 750 std::vector<std::unique_ptr<Action>> actions{}; 751 actions.push_back(std::make_unique<MockAction>()); 752 753 PhaseFaultDetection detection{std::move(actions)}; 754 EXPECT_EQ(detection.getDeviceID(), ""); 755 } 756 757 // Test where device ID not specified 758 { 759 std::vector<std::unique_ptr<Action>> actions{}; 760 actions.push_back(std::make_unique<MockAction>()); 761 762 PhaseFaultDetection detection{std::move(actions), "ioexp1"}; 763 EXPECT_EQ(detection.getDeviceID(), "ioexp1"); 764 } 765 } 766