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