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_journal.hpp" 23 #include "mock_services.hpp" 24 #include "mocked_i2c_interface.hpp" 25 #include "pmbus_read_sensor_action.hpp" 26 #include "presence_detection.hpp" 27 #include "rail.hpp" 28 #include "rule.hpp" 29 #include "sensors.hpp" 30 #include "system.hpp" 31 #include "test_utils.hpp" 32 33 #include <memory> 34 #include <stdexcept> 35 #include <string> 36 #include <utility> 37 #include <vector> 38 39 #include <gmock/gmock.h> 40 #include <gtest/gtest.h> 41 42 using namespace phosphor::power::regulators; 43 using namespace phosphor::power::regulators::test_utils; 44 45 using ::testing::A; 46 using ::testing::Return; 47 using ::testing::TypedEq; 48 49 // Default chassis inventory path 50 static const std::string defaultInventoryPath{ 51 "/xyz/openbmc_project/inventory/system/chassis"}; 52 53 TEST(ChassisTests, Constructor) 54 { 55 // Test where works: Only required parameters are specified 56 { 57 Chassis chassis{2, defaultInventoryPath}; 58 EXPECT_EQ(chassis.getNumber(), 2); 59 EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath); 60 EXPECT_EQ(chassis.getDevices().size(), 0); 61 } 62 63 // Test where works: All parameters are specified 64 { 65 // Create vector of Device objects 66 std::vector<std::unique_ptr<Device>> devices{}; 67 devices.emplace_back(createDevice("vdd_reg1")); 68 devices.emplace_back(createDevice("vdd_reg2")); 69 70 // Create Chassis 71 Chassis chassis{1, defaultInventoryPath, std::move(devices)}; 72 EXPECT_EQ(chassis.getNumber(), 1); 73 EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath); 74 EXPECT_EQ(chassis.getDevices().size(), 2); 75 } 76 77 // Test where fails: Invalid chassis number < 1 78 try 79 { 80 Chassis chassis{0, defaultInventoryPath}; 81 ADD_FAILURE() << "Should not have reached this line."; 82 } 83 catch (const std::invalid_argument& e) 84 { 85 EXPECT_STREQ(e.what(), "Invalid chassis number: 0"); 86 } 87 catch (...) 88 { 89 ADD_FAILURE() << "Should not have caught exception."; 90 } 91 } 92 93 TEST(ChassisTests, AddToIDMap) 94 { 95 // Create vector of Device objects 96 std::vector<std::unique_ptr<Device>> devices{}; 97 devices.emplace_back(createDevice("reg1", {"rail1"})); 98 devices.emplace_back(createDevice("reg2", {"rail2a", "rail2b"})); 99 devices.emplace_back(createDevice("reg3")); 100 101 // Create Chassis 102 Chassis chassis{1, defaultInventoryPath, std::move(devices)}; 103 104 // Add Device and Rail objects within the Chassis to an IDMap 105 IDMap idMap{}; 106 chassis.addToIDMap(idMap); 107 108 // Verify all Devices are in the IDMap 109 EXPECT_NO_THROW(idMap.getDevice("reg1")); 110 EXPECT_NO_THROW(idMap.getDevice("reg2")); 111 EXPECT_NO_THROW(idMap.getDevice("reg3")); 112 EXPECT_THROW(idMap.getDevice("reg4"), std::invalid_argument); 113 114 // Verify all Rails are in the IDMap 115 EXPECT_NO_THROW(idMap.getRail("rail1")); 116 EXPECT_NO_THROW(idMap.getRail("rail2a")); 117 EXPECT_NO_THROW(idMap.getRail("rail2b")); 118 EXPECT_THROW(idMap.getRail("rail3"), std::invalid_argument); 119 } 120 121 TEST(ChassisTests, ClearCache) 122 { 123 // Create PresenceDetection 124 std::vector<std::unique_ptr<Action>> actions{}; 125 std::unique_ptr<PresenceDetection> presenceDetection = 126 std::make_unique<PresenceDetection>(std::move(actions)); 127 PresenceDetection* presenceDetectionPtr = presenceDetection.get(); 128 129 // Create Device that contains PresenceDetection 130 std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface(); 131 std::unique_ptr<Device> device = std::make_unique<Device>( 132 "reg1", true, 133 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 134 std::move(i2cInterface), std::move(presenceDetection)); 135 Device* devicePtr = device.get(); 136 137 // Create Chassis that contains Device 138 std::vector<std::unique_ptr<Device>> devices{}; 139 devices.emplace_back(std::move(device)); 140 std::unique_ptr<Chassis> chassis = 141 std::make_unique<Chassis>(1, defaultInventoryPath, std::move(devices)); 142 Chassis* chassisPtr = chassis.get(); 143 144 // Create System that contains Chassis 145 std::vector<std::unique_ptr<Rule>> rules{}; 146 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 147 chassisVec.emplace_back(std::move(chassis)); 148 System system{std::move(rules), std::move(chassisVec)}; 149 150 // Cache presence value in PresenceDetection 151 MockServices services{}; 152 presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr); 153 EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value()); 154 155 // Clear cached data in Chassis 156 chassisPtr->clearCache(); 157 158 // Verify presence value no longer cached in PresenceDetection 159 EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value()); 160 } 161 162 TEST(ChassisTests, CloseDevices) 163 { 164 // Test where no devices were specified in constructor 165 { 166 // Create mock services. Expect logDebug() to be called. 167 MockServices services{}; 168 MockJournal& journal = services.getMockJournal(); 169 EXPECT_CALL(journal, logDebug("Closing devices in chassis 2")).Times(1); 170 171 // Create Chassis 172 Chassis chassis{2, defaultInventoryPath}; 173 174 // Call closeDevices() 175 chassis.closeDevices(services); 176 } 177 178 // Test where devices were specified in constructor 179 { 180 std::vector<std::unique_ptr<Device>> devices{}; 181 182 // Create mock services. Expect logDebug() to be called. 183 MockServices services{}; 184 MockJournal& journal = services.getMockJournal(); 185 EXPECT_CALL(journal, logDebug("Closing devices in chassis 1")).Times(1); 186 187 // Create Device vdd0_reg 188 { 189 // Create mock I2CInterface: isOpen() and close() should be called 190 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 191 std::make_unique<i2c::MockedI2CInterface>(); 192 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 193 EXPECT_CALL(*i2cInterface, close).Times(1); 194 195 // Create Device 196 std::unique_ptr<Device> device = 197 std::make_unique<Device>("vdd0_reg", true, 198 "/xyz/openbmc_project/inventory/" 199 "system/chassis/motherboard/vdd0_reg", 200 std::move(i2cInterface)); 201 devices.emplace_back(std::move(device)); 202 } 203 204 // Create Device vdd1_reg 205 { 206 // Create mock I2CInterface: isOpen() and close() should be called 207 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 208 std::make_unique<i2c::MockedI2CInterface>(); 209 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 210 EXPECT_CALL(*i2cInterface, close).Times(1); 211 212 // Create Device 213 std::unique_ptr<Device> device = 214 std::make_unique<Device>("vdd1_reg", true, 215 "/xyz/openbmc_project/inventory/" 216 "system/chassis/motherboard/vdd1_reg", 217 std::move(i2cInterface)); 218 devices.emplace_back(std::move(device)); 219 } 220 221 // Create Chassis 222 Chassis chassis{1, defaultInventoryPath, std::move(devices)}; 223 224 // Call closeDevices() 225 chassis.closeDevices(services); 226 } 227 } 228 229 TEST(ChassisTests, Configure) 230 { 231 // Test where no devices were specified in constructor 232 { 233 // Create mock services. Expect logInfo() to be called. 234 MockServices services{}; 235 MockJournal& journal = services.getMockJournal(); 236 EXPECT_CALL(journal, logInfo("Configuring chassis 1")).Times(1); 237 EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0); 238 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 239 240 // Create Chassis 241 std::unique_ptr<Chassis> chassis = 242 std::make_unique<Chassis>(1, defaultInventoryPath); 243 Chassis* chassisPtr = chassis.get(); 244 245 // Create System that contains Chassis 246 std::vector<std::unique_ptr<Rule>> rules{}; 247 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 248 chassisVec.emplace_back(std::move(chassis)); 249 System system{std::move(rules), std::move(chassisVec)}; 250 251 // Call configure() 252 chassisPtr->configure(services, system); 253 } 254 255 // Test where devices were specified in constructor 256 { 257 std::vector<std::unique_ptr<Device>> devices{}; 258 259 // Create mock services. Expect logInfo() and logDebug() to be called. 260 MockServices services{}; 261 MockJournal& journal = services.getMockJournal(); 262 EXPECT_CALL(journal, logInfo("Configuring chassis 2")).Times(1); 263 EXPECT_CALL(journal, logDebug("Configuring vdd0_reg: volts=1.300000")) 264 .Times(1); 265 EXPECT_CALL(journal, logDebug("Configuring vdd1_reg: volts=1.200000")) 266 .Times(1); 267 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 268 269 // Create Device vdd0_reg 270 { 271 // Create Configuration 272 std::vector<std::unique_ptr<Action>> actions{}; 273 std::unique_ptr<Configuration> configuration = 274 std::make_unique<Configuration>(1.3, std::move(actions)); 275 276 // Create Device 277 std::unique_ptr<i2c::I2CInterface> i2cInterface = 278 createI2CInterface(); 279 std::unique_ptr<PresenceDetection> presenceDetection{}; 280 std::unique_ptr<Device> device = std::make_unique<Device>( 281 "vdd0_reg", true, 282 "/xyz/openbmc_project/inventory/system/chassis/motherboard/" 283 "vdd0_reg", 284 std::move(i2cInterface), std::move(presenceDetection), 285 std::move(configuration)); 286 devices.emplace_back(std::move(device)); 287 } 288 289 // Create Device vdd1_reg 290 { 291 // Create Configuration 292 std::vector<std::unique_ptr<Action>> actions{}; 293 std::unique_ptr<Configuration> configuration = 294 std::make_unique<Configuration>(1.2, std::move(actions)); 295 296 // Create Device 297 std::unique_ptr<i2c::I2CInterface> i2cInterface = 298 createI2CInterface(); 299 std::unique_ptr<PresenceDetection> presenceDetection{}; 300 std::unique_ptr<Device> device = std::make_unique<Device>( 301 "vdd1_reg", true, 302 "/xyz/openbmc_project/inventory/system/chassis/motherboard/" 303 "vdd1_reg", 304 std::move(i2cInterface), std::move(presenceDetection), 305 std::move(configuration)); 306 devices.emplace_back(std::move(device)); 307 } 308 309 // Create Chassis 310 std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>( 311 2, defaultInventoryPath, std::move(devices)); 312 Chassis* chassisPtr = chassis.get(); 313 314 // Create System that contains Chassis 315 std::vector<std::unique_ptr<Rule>> rules{}; 316 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 317 chassisVec.emplace_back(std::move(chassis)); 318 System system{std::move(rules), std::move(chassisVec)}; 319 320 // Call configure() 321 chassisPtr->configure(services, system); 322 } 323 } 324 325 TEST(ChassisTests, GetDevices) 326 { 327 // Test where no devices were specified in constructor 328 { 329 Chassis chassis{2, defaultInventoryPath}; 330 EXPECT_EQ(chassis.getDevices().size(), 0); 331 } 332 333 // Test where devices were specified in constructor 334 { 335 // Create vector of Device objects 336 std::vector<std::unique_ptr<Device>> devices{}; 337 devices.emplace_back(createDevice("vdd_reg1")); 338 devices.emplace_back(createDevice("vdd_reg2")); 339 340 // Create Chassis 341 Chassis chassis{1, defaultInventoryPath, std::move(devices)}; 342 EXPECT_EQ(chassis.getDevices().size(), 2); 343 EXPECT_EQ(chassis.getDevices()[0]->getID(), "vdd_reg1"); 344 EXPECT_EQ(chassis.getDevices()[1]->getID(), "vdd_reg2"); 345 } 346 } 347 348 TEST(ChassisTests, GetInventoryPath) 349 { 350 Chassis chassis{3, defaultInventoryPath}; 351 EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath); 352 } 353 354 TEST(ChassisTests, GetNumber) 355 { 356 Chassis chassis{3, defaultInventoryPath}; 357 EXPECT_EQ(chassis.getNumber(), 3); 358 } 359 360 TEST(ChassisTests, MonitorSensors) 361 { 362 // Test where no devices were specified in constructor 363 { 364 // Create mock services. No logging should occur. 365 MockServices services{}; 366 MockJournal& journal = services.getMockJournal(); 367 EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0); 368 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 369 370 // Create Chassis 371 std::vector<std::unique_ptr<Device>> devices{}; 372 std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>( 373 1, defaultInventoryPath, std::move(devices)); 374 Chassis* chassisPtr = chassis.get(); 375 376 // Create System that contains Chassis 377 std::vector<std::unique_ptr<Rule>> rules{}; 378 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 379 chassisVec.emplace_back(std::move(chassis)); 380 System system{std::move(rules), std::move(chassisVec)}; 381 382 // Call monitorSensors(). Should do nothing. 383 chassisPtr->monitorSensors(services, system); 384 } 385 386 // Test where devices were specified in constructor 387 { 388 // Create mock services. No logging should occur. 389 MockServices services{}; 390 MockJournal& journal = services.getMockJournal(); 391 EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0); 392 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 393 394 std::vector<std::unique_ptr<Device>> devices{}; 395 396 // Create PMBusReadSensorAction 397 SensorType type{SensorType::iout}; 398 uint8_t command = 0x8C; 399 pmbus_utils::SensorDataFormat format{ 400 pmbus_utils::SensorDataFormat::linear_11}; 401 std::optional<int8_t> exponent{}; 402 std::unique_ptr<PMBusReadSensorAction> action = 403 std::make_unique<PMBusReadSensorAction>(type, command, format, 404 exponent); 405 406 // Create mock I2CInterface. A two-byte read should occur. 407 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 408 std::make_unique<i2c::MockedI2CInterface>(); 409 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 410 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>())) 411 .Times(1); 412 413 // Create SensorMonitoring 414 std::vector<std::unique_ptr<Action>> actions{}; 415 actions.emplace_back(std::move(action)); 416 std::unique_ptr<SensorMonitoring> sensorMonitoring = 417 std::make_unique<SensorMonitoring>(std::move(actions)); 418 419 // Create Rail 420 std::vector<std::unique_ptr<Rail>> rails{}; 421 std::unique_ptr<Configuration> configuration{}; 422 std::unique_ptr<Rail> rail = std::make_unique<Rail>( 423 "vdd0", std::move(configuration), std::move(sensorMonitoring)); 424 rails.emplace_back(std::move(rail)); 425 426 // Create Device 427 std::unique_ptr<PresenceDetection> presenceDetection{}; 428 std::unique_ptr<Configuration> deviceConfiguration{}; 429 std::unique_ptr<Device> device = std::make_unique<Device>( 430 "reg1", true, 431 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 432 std::move(i2cInterface), std::move(presenceDetection), 433 std::move(deviceConfiguration), std::move(rails)); 434 435 // Create Chassis 436 devices.emplace_back(std::move(device)); 437 std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>( 438 1, defaultInventoryPath, std::move(devices)); 439 Chassis* chassisPtr = chassis.get(); 440 441 // Create System that contains Chassis 442 std::vector<std::unique_ptr<Rule>> rules{}; 443 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 444 chassisVec.emplace_back(std::move(chassis)); 445 System system{std::move(rules), std::move(chassisVec)}; 446 447 // Call monitorSensors() 448 chassisPtr->monitorSensors(services, system); 449 } 450 } 451