1 /** 2 * Copyright © 2020 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 "action.hpp" 17 #include "chassis.hpp" 18 #include "configuration.hpp" 19 #include "device.hpp" 20 #include "i2c_interface.hpp" 21 #include "id_map.hpp" 22 #include "log_phase_fault_action.hpp" 23 #include "mock_action.hpp" 24 #include "mock_error_logging.hpp" 25 #include "mock_journal.hpp" 26 #include "mock_sensors.hpp" 27 #include "mock_services.hpp" 28 #include "mocked_i2c_interface.hpp" 29 #include "phase_fault.hpp" 30 #include "phase_fault_detection.hpp" 31 #include "presence_detection.hpp" 32 #include "rail.hpp" 33 #include "rule.hpp" 34 #include "sensor_monitoring.hpp" 35 #include "sensors.hpp" 36 #include "services.hpp" 37 #include "system.hpp" 38 #include "test_sdbus_error.hpp" 39 #include "test_utils.hpp" 40 41 #include <memory> 42 #include <stdexcept> 43 #include <string> 44 #include <utility> 45 #include <vector> 46 47 #include <gmock/gmock.h> 48 #include <gtest/gtest.h> 49 50 using namespace phosphor::power::regulators; 51 using namespace phosphor::power::regulators::test_utils; 52 53 using ::testing::A; 54 using ::testing::Return; 55 using ::testing::Throw; 56 using ::testing::TypedEq; 57 58 static const std::string chassisInvPath{ 59 "/xyz/openbmc_project/inventory/system/chassis"}; 60 61 TEST(SystemTests, Constructor) 62 { 63 // Create Rules 64 std::vector<std::unique_ptr<Rule>> rules{}; 65 rules.emplace_back(createRule("set_voltage_rule")); 66 67 // Create Chassis 68 std::vector<std::unique_ptr<Chassis>> chassis{}; 69 std::vector<std::unique_ptr<Device>> devices{}; 70 devices.emplace_back(createDevice("reg1", {"rail1"})); 71 chassis.emplace_back( 72 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices))); 73 74 // Create System 75 System system{std::move(rules), std::move(chassis)}; 76 EXPECT_EQ(system.getChassis().size(), 1); 77 EXPECT_EQ(system.getChassis()[0]->getNumber(), 1); 78 EXPECT_NO_THROW(system.getIDMap().getRule("set_voltage_rule")); 79 EXPECT_NO_THROW(system.getIDMap().getDevice("reg1")); 80 EXPECT_NO_THROW(system.getIDMap().getRail("rail1")); 81 EXPECT_THROW(system.getIDMap().getRail("rail2"), std::invalid_argument); 82 EXPECT_EQ(system.getRules().size(), 1); 83 EXPECT_EQ(system.getRules()[0]->getID(), "set_voltage_rule"); 84 } 85 86 TEST(SystemTests, ClearCache) 87 { 88 // Create PresenceDetection 89 std::vector<std::unique_ptr<Action>> actions{}; 90 auto presenceDetection = 91 std::make_unique<PresenceDetection>(std::move(actions)); 92 PresenceDetection* presenceDetectionPtr = presenceDetection.get(); 93 94 // Create Device that contains PresenceDetection 95 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 96 auto device = std::make_unique<Device>( 97 "reg1", true, 98 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 99 std::move(i2cInterface), std::move(presenceDetection)); 100 Device* devicePtr = device.get(); 101 102 // Create Chassis that contains Device 103 std::vector<std::unique_ptr<Device>> devices{}; 104 devices.emplace_back(std::move(device)); 105 auto chassis = std::make_unique<Chassis>(1, chassisInvPath, 106 std::move(devices)); 107 Chassis* chassisPtr = chassis.get(); 108 109 // Create System that contains Chassis 110 std::vector<std::unique_ptr<Rule>> rules{}; 111 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 112 chassisVec.emplace_back(std::move(chassis)); 113 System system{std::move(rules), std::move(chassisVec)}; 114 115 // Cache presence value in PresenceDetection 116 MockServices services{}; 117 presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr); 118 EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value()); 119 120 // Clear cached data in System 121 system.clearCache(); 122 123 // Verify presence value no longer cached in PresenceDetection 124 EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value()); 125 } 126 127 TEST(SystemTests, ClearErrorHistory) 128 { 129 // Create SensorMonitoring. Will fail with a DBus exception. 130 auto action = std::make_unique<MockAction>(); 131 EXPECT_CALL(*action, execute) 132 .WillRepeatedly(Throw(TestSDBusError{"Unable to set sensor value"})); 133 std::vector<std::unique_ptr<Action>> actions{}; 134 actions.emplace_back(std::move(action)); 135 auto sensorMonitoring = 136 std::make_unique<SensorMonitoring>(std::move(actions)); 137 138 // Create Rail 139 std::unique_ptr<Configuration> configuration{}; 140 auto rail = std::make_unique<Rail>("vddr1", std::move(configuration), 141 std::move(sensorMonitoring)); 142 143 // Create Device that contains Rail 144 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 145 std::unique_ptr<PresenceDetection> presenceDetection{}; 146 std::unique_ptr<Configuration> deviceConfiguration{}; 147 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 148 std::vector<std::unique_ptr<Rail>> rails{}; 149 rails.emplace_back(std::move(rail)); 150 auto device = std::make_unique<Device>( 151 "reg1", true, 152 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 153 std::move(i2cInterface), std::move(presenceDetection), 154 std::move(deviceConfiguration), std::move(phaseFaultDetection), 155 std::move(rails)); 156 157 // Create Chassis that contains Device 158 std::vector<std::unique_ptr<Device>> devices{}; 159 devices.emplace_back(std::move(device)); 160 auto chassis = std::make_unique<Chassis>(1, chassisInvPath, 161 std::move(devices)); 162 163 // Create System that contains Chassis 164 std::vector<std::unique_ptr<Rule>> rules{}; 165 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 166 chassisVec.emplace_back(std::move(chassis)); 167 System system{std::move(rules), std::move(chassisVec)}; 168 169 // Create lambda that sets MockServices expectations. The lambda allows 170 // us to set expectations multiple times without duplicate code. 171 auto setExpectations = [](MockServices& services) { 172 // Expect Sensors service to be called 10 times 173 MockSensors& sensors = services.getMockSensors(); 174 EXPECT_CALL(sensors, startRail).Times(10); 175 EXPECT_CALL(sensors, setValue).Times(0); 176 EXPECT_CALL(sensors, endRail).Times(10); 177 178 // Expect Journal service to be called 6 times to log error messages 179 MockJournal& journal = services.getMockJournal(); 180 EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>())) 181 .Times(6); 182 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(6); 183 184 // Expect ErrorLogging service to be called once to log a DBus error 185 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 186 EXPECT_CALL(errorLogging, logDBusError).Times(1); 187 }; 188 189 // Monitor sensors 10 times. Verify errors logged. 190 { 191 // Create mock services. Set expectations via lambda. 192 MockServices services{}; 193 setExpectations(services); 194 195 for (int i = 1; i <= 10; ++i) 196 { 197 system.monitorSensors(services); 198 } 199 } 200 201 // Clear error history 202 system.clearErrorHistory(); 203 204 // Monitor sensors 10 more times. Verify errors logged again. 205 { 206 // Create mock services. Set expectations via lambda. 207 MockServices services{}; 208 setExpectations(services); 209 210 for (int i = 1; i <= 10; ++i) 211 { 212 system.monitorSensors(services); 213 } 214 } 215 } 216 217 TEST(SystemTests, CloseDevices) 218 { 219 // Specify an empty rules vector 220 std::vector<std::unique_ptr<Rule>> rules{}; 221 222 // Create mock services. Expect logDebug() to be called. 223 MockServices services{}; 224 MockJournal& journal = services.getMockJournal(); 225 EXPECT_CALL(journal, logDebug("Closing devices in chassis 1")).Times(1); 226 EXPECT_CALL(journal, logDebug("Closing devices in chassis 3")).Times(1); 227 EXPECT_CALL(journal, logInfo(A<const std::string&>())).Times(0); 228 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 229 230 // Create Chassis 231 std::vector<std::unique_ptr<Chassis>> chassis{}; 232 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1')); 233 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3')); 234 235 // Create System 236 System system{std::move(rules), std::move(chassis)}; 237 238 // Call closeDevices() 239 system.closeDevices(services); 240 } 241 242 TEST(SystemTests, Configure) 243 { 244 // Create mock services. Expect logInfo() to be called. 245 MockServices services{}; 246 MockJournal& journal = services.getMockJournal(); 247 EXPECT_CALL(journal, logInfo("Configuring chassis 1")).Times(1); 248 EXPECT_CALL(journal, logInfo("Configuring chassis 3")).Times(1); 249 EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0); 250 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 251 252 // Specify an empty rules vector 253 std::vector<std::unique_ptr<Rule>> rules{}; 254 255 // Create Chassis 256 std::vector<std::unique_ptr<Chassis>> chassis{}; 257 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1')); 258 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3')); 259 260 // Create System 261 System system{std::move(rules), std::move(chassis)}; 262 263 // Call configure() 264 system.configure(services); 265 } 266 267 TEST(SystemTests, DetectPhaseFaults) 268 { 269 // Create mock services with the following expectations: 270 // - 2 error messages in journal for N phase fault detected in reg0 271 // - 2 error messages in journal for N phase fault detected in reg1 272 // - 1 N phase fault error logged for reg0 273 // - 1 N phase fault error logged for reg1 274 MockServices services{}; 275 MockJournal& journal = services.getMockJournal(); 276 EXPECT_CALL(journal, 277 logError("n phase fault detected in regulator reg0: count=1")) 278 .Times(1); 279 EXPECT_CALL(journal, 280 logError("n phase fault detected in regulator reg0: count=2")) 281 .Times(1); 282 EXPECT_CALL(journal, 283 logError("n phase fault detected in regulator reg1: count=1")) 284 .Times(1); 285 EXPECT_CALL(journal, 286 logError("n phase fault detected in regulator reg1: count=2")) 287 .Times(1); 288 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 289 EXPECT_CALL(errorLogging, logPhaseFault).Times(2); 290 291 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 292 293 // Create Chassis 1 with regulator reg0 294 { 295 // Create PhaseFaultDetection 296 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n); 297 std::vector<std::unique_ptr<Action>> actions{}; 298 actions.push_back(std::move(action)); 299 auto phaseFaultDetection = 300 std::make_unique<PhaseFaultDetection>(std::move(actions)); 301 302 // Create Device 303 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 304 std::unique_ptr<PresenceDetection> presenceDetection{}; 305 std::unique_ptr<Configuration> configuration{}; 306 auto device = std::make_unique<Device>( 307 "reg0", true, 308 "/xyz/openbmc_project/inventory/system/chassis1/motherboard/" 309 "reg0", 310 std::move(i2cInterface), std::move(presenceDetection), 311 std::move(configuration), std::move(phaseFaultDetection)); 312 313 // Create Chassis 314 std::vector<std::unique_ptr<Device>> devices{}; 315 devices.emplace_back(std::move(device)); 316 auto chassis = std::make_unique<Chassis>(1, chassisInvPath + '1', 317 std::move(devices)); 318 chassisVec.emplace_back(std::move(chassis)); 319 } 320 321 // Create Chassis 2 with regulator reg1 322 { 323 // Create PhaseFaultDetection 324 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n); 325 std::vector<std::unique_ptr<Action>> actions{}; 326 actions.push_back(std::move(action)); 327 auto phaseFaultDetection = 328 std::make_unique<PhaseFaultDetection>(std::move(actions)); 329 330 // Create Device 331 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 332 std::unique_ptr<PresenceDetection> presenceDetection{}; 333 std::unique_ptr<Configuration> configuration{}; 334 auto device = std::make_unique<Device>( 335 "reg1", true, 336 "/xyz/openbmc_project/inventory/system/chassis2/motherboard/" 337 "reg1", 338 std::move(i2cInterface), std::move(presenceDetection), 339 std::move(configuration), std::move(phaseFaultDetection)); 340 341 // Create Chassis 342 std::vector<std::unique_ptr<Device>> devices{}; 343 devices.emplace_back(std::move(device)); 344 auto chassis = std::make_unique<Chassis>(2, chassisInvPath + '2', 345 std::move(devices)); 346 chassisVec.emplace_back(std::move(chassis)); 347 } 348 349 // Create System that contains Chassis 350 std::vector<std::unique_ptr<Rule>> rules{}; 351 System system{std::move(rules), std::move(chassisVec)}; 352 353 // Call detectPhaseFaults() 5 times 354 for (int i = 1; i <= 5; ++i) 355 { 356 system.detectPhaseFaults(services); 357 } 358 } 359 360 TEST(SystemTests, GetChassis) 361 { 362 // Specify an empty rules vector 363 std::vector<std::unique_ptr<Rule>> rules{}; 364 365 // Create Chassis 366 std::vector<std::unique_ptr<Chassis>> chassis{}; 367 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1')); 368 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3')); 369 370 // Create System 371 System system{std::move(rules), std::move(chassis)}; 372 EXPECT_EQ(system.getChassis().size(), 2); 373 EXPECT_EQ(system.getChassis()[0]->getNumber(), 1); 374 EXPECT_EQ(system.getChassis()[1]->getNumber(), 3); 375 } 376 377 TEST(SystemTests, GetIDMap) 378 { 379 // Create Rules 380 std::vector<std::unique_ptr<Rule>> rules{}; 381 rules.emplace_back(createRule("set_voltage_rule")); 382 rules.emplace_back(createRule("read_sensors_rule")); 383 384 // Create Chassis 385 std::vector<std::unique_ptr<Chassis>> chassis{}; 386 { 387 // Chassis 1 388 std::vector<std::unique_ptr<Device>> devices{}; 389 devices.emplace_back(createDevice("reg1", {"rail1"})); 390 devices.emplace_back(createDevice("reg2", {"rail2a", "rail2b"})); 391 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1', 392 std::move(devices))); 393 } 394 { 395 // Chassis 2 396 std::vector<std::unique_ptr<Device>> devices{}; 397 devices.emplace_back(createDevice("reg3", {"rail3a", "rail3b"})); 398 devices.emplace_back(createDevice("reg4")); 399 chassis.emplace_back(std::make_unique<Chassis>(2, chassisInvPath + '2', 400 std::move(devices))); 401 } 402 403 // Create System 404 System system{std::move(rules), std::move(chassis)}; 405 const IDMap& idMap = system.getIDMap(); 406 407 // Verify all Rules are in the IDMap 408 EXPECT_NO_THROW(idMap.getRule("set_voltage_rule")); 409 EXPECT_NO_THROW(idMap.getRule("read_sensors_rule")); 410 EXPECT_THROW(idMap.getRule("set_voltage_rule2"), std::invalid_argument); 411 412 // Verify all Devices are in the IDMap 413 EXPECT_NO_THROW(idMap.getDevice("reg1")); 414 EXPECT_NO_THROW(idMap.getDevice("reg2")); 415 EXPECT_NO_THROW(idMap.getDevice("reg3")); 416 EXPECT_NO_THROW(idMap.getDevice("reg4")); 417 EXPECT_THROW(idMap.getDevice("reg5"), std::invalid_argument); 418 419 // Verify all Rails are in the IDMap 420 EXPECT_NO_THROW(idMap.getRail("rail1")); 421 EXPECT_NO_THROW(idMap.getRail("rail2a")); 422 EXPECT_NO_THROW(idMap.getRail("rail2b")); 423 EXPECT_NO_THROW(idMap.getRail("rail3a")); 424 EXPECT_NO_THROW(idMap.getRail("rail3b")); 425 EXPECT_THROW(idMap.getRail("rail4"), std::invalid_argument); 426 } 427 428 TEST(SystemTests, GetRules) 429 { 430 // Create Rules 431 std::vector<std::unique_ptr<Rule>> rules{}; 432 rules.emplace_back(createRule("set_voltage_rule")); 433 rules.emplace_back(createRule("read_sensors_rule")); 434 435 // Create Chassis 436 std::vector<std::unique_ptr<Chassis>> chassis{}; 437 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath)); 438 439 // Create System 440 System system{std::move(rules), std::move(chassis)}; 441 EXPECT_EQ(system.getRules().size(), 2); 442 EXPECT_EQ(system.getRules()[0]->getID(), "set_voltage_rule"); 443 EXPECT_EQ(system.getRules()[1]->getID(), "read_sensors_rule"); 444 } 445 446 TEST(SystemTests, MonitorSensors) 447 { 448 // Create mock services. Set Sensors service expectations. 449 MockServices services{}; 450 MockSensors& sensors = services.getMockSensors(); 451 EXPECT_CALL(sensors, startRail("c1_vdd0", 452 "/xyz/openbmc_project/inventory/system/" 453 "chassis1/motherboard/vdd0_reg", 454 chassisInvPath + '1')) 455 .Times(1); 456 EXPECT_CALL(sensors, startRail("c2_vdd0", 457 "/xyz/openbmc_project/inventory/system/" 458 "chassis2/motherboard/vdd0_reg", 459 chassisInvPath + '2')) 460 .Times(1); 461 EXPECT_CALL(sensors, setValue).Times(0); 462 EXPECT_CALL(sensors, endRail(false)).Times(2); 463 464 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 465 466 // Create Chassis 1 467 { 468 // Create SensorMonitoring for Rail 469 auto action = std::make_unique<MockAction>(); 470 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true)); 471 std::vector<std::unique_ptr<Action>> actions{}; 472 actions.emplace_back(std::move(action)); 473 auto sensorMonitoring = 474 std::make_unique<SensorMonitoring>(std::move(actions)); 475 476 // Create Rail 477 std::unique_ptr<Configuration> configuration{}; 478 auto rail = std::make_unique<Rail>("c1_vdd0", std::move(configuration), 479 std::move(sensorMonitoring)); 480 481 // Create Device 482 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 483 std::unique_ptr<PresenceDetection> presenceDetection{}; 484 std::unique_ptr<Configuration> deviceConfiguration{}; 485 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 486 std::vector<std::unique_ptr<Rail>> rails{}; 487 rails.emplace_back(std::move(rail)); 488 auto device = std::make_unique<Device>( 489 "c1_vdd0_reg", true, 490 "/xyz/openbmc_project/inventory/system/chassis1/motherboard/" 491 "vdd0_reg", 492 std::move(i2cInterface), std::move(presenceDetection), 493 std::move(deviceConfiguration), std::move(phaseFaultDetection), 494 std::move(rails)); 495 496 // Create Chassis 497 std::vector<std::unique_ptr<Device>> devices{}; 498 devices.emplace_back(std::move(device)); 499 auto chassis = std::make_unique<Chassis>(1, chassisInvPath + '1', 500 std::move(devices)); 501 chassisVec.emplace_back(std::move(chassis)); 502 } 503 504 // Create Chassis 2 505 { 506 // Create SensorMonitoring for Rail 507 auto action = std::make_unique<MockAction>(); 508 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true)); 509 std::vector<std::unique_ptr<Action>> actions{}; 510 actions.emplace_back(std::move(action)); 511 auto sensorMonitoring = 512 std::make_unique<SensorMonitoring>(std::move(actions)); 513 514 // Create Rail 515 std::unique_ptr<Configuration> configuration{}; 516 auto rail = std::make_unique<Rail>("c2_vdd0", std::move(configuration), 517 std::move(sensorMonitoring)); 518 519 // Create Device 520 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>(); 521 std::unique_ptr<PresenceDetection> presenceDetection{}; 522 std::unique_ptr<Configuration> deviceConfiguration{}; 523 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 524 std::vector<std::unique_ptr<Rail>> rails{}; 525 rails.emplace_back(std::move(rail)); 526 auto device = std::make_unique<Device>( 527 "c2_vdd0_reg", true, 528 "/xyz/openbmc_project/inventory/system/chassis2/motherboard/" 529 "vdd0_reg", 530 std::move(i2cInterface), std::move(presenceDetection), 531 std::move(deviceConfiguration), std::move(phaseFaultDetection), 532 std::move(rails)); 533 534 // Create Chassis 535 std::vector<std::unique_ptr<Device>> devices{}; 536 devices.emplace_back(std::move(device)); 537 auto chassis = std::make_unique<Chassis>(2, chassisInvPath + '2', 538 std::move(devices)); 539 chassisVec.emplace_back(std::move(chassis)); 540 } 541 542 // Create System that contains Chassis 543 std::vector<std::unique_ptr<Rule>> rules{}; 544 System system{std::move(rules), std::move(chassisVec)}; 545 546 // Call monitorSensors() 547 system.monitorSensors(services); 548 } 549