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