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 "i2c_write_byte_action.hpp" 22 #include "mock_action.hpp" 23 #include "mock_journal.hpp" 24 #include "mock_services.hpp" 25 #include "mocked_i2c_interface.hpp" 26 #include "pmbus_utils.hpp" 27 #include "pmbus_write_vout_command_action.hpp" 28 #include "presence_detection.hpp" 29 #include "rail.hpp" 30 #include "rule.hpp" 31 #include "system.hpp" 32 33 #include <cstdint> 34 #include <memory> 35 #include <optional> 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::pmbus_utils; 44 45 using ::testing::A; 46 using ::testing::Return; 47 using ::testing::Throw; 48 using ::testing::TypedEq; 49 50 TEST(ConfigurationTests, Constructor) 51 { 52 // Test where volts value specified 53 { 54 std::optional<double> volts{1.3}; 55 56 std::vector<std::unique_ptr<Action>> actions{}; 57 actions.push_back(std::make_unique<MockAction>()); 58 actions.push_back(std::make_unique<MockAction>()); 59 60 Configuration configuration(volts, std::move(actions)); 61 EXPECT_EQ(configuration.getVolts().has_value(), true); 62 EXPECT_EQ(configuration.getVolts().value(), 1.3); 63 EXPECT_EQ(configuration.getActions().size(), 2); 64 } 65 66 // Test where volts value not specified 67 { 68 std::optional<double> volts{}; 69 70 std::vector<std::unique_ptr<Action>> actions{}; 71 actions.push_back(std::make_unique<MockAction>()); 72 73 Configuration configuration(volts, std::move(actions)); 74 EXPECT_EQ(configuration.getVolts().has_value(), false); 75 EXPECT_EQ(configuration.getActions().size(), 1); 76 } 77 } 78 79 // Test for execute(Services&, System&, Chassis&, Device&) 80 TEST(ConfigurationTests, ExecuteForDevice) 81 { 82 // Test where works: Volts value not specified 83 { 84 // Create mock services. Expect logDebug() to be called. 85 MockServices services{}; 86 MockJournal& journal = services.getMockJournal(); 87 EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1); 88 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 89 90 // Create I2CWriteByteAction with register 0x7C and value 0x0A 91 std::unique_ptr<I2CWriteByteAction> action = 92 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 93 94 // Create mock I2CInterface. Expect action to write 0x0A to 0x7C. 95 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 96 std::make_unique<i2c::MockedI2CInterface>(); 97 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 98 EXPECT_CALL(*i2cInterface, 99 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 100 .Times(1); 101 102 // Create Configuration with no volts value specified 103 std::optional<double> volts{}; 104 std::vector<std::unique_ptr<Action>> actions{}; 105 actions.emplace_back(std::move(action)); 106 std::unique_ptr<Configuration> configuration = 107 std::make_unique<Configuration>(volts, std::move(actions)); 108 Configuration* configurationPtr = configuration.get(); 109 110 // Create Device that contains Configuration 111 std::unique_ptr<PresenceDetection> presenceDetection{}; 112 std::unique_ptr<Device> device = std::make_unique<Device>( 113 "vdd_reg", true, 114 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 115 std::move(i2cInterface), std::move(presenceDetection), 116 std::move(configuration)); 117 Device* devicePtr = device.get(); 118 119 // Create Chassis that contains Device 120 std::vector<std::unique_ptr<Device>> devices{}; 121 devices.emplace_back(std::move(device)); 122 std::unique_ptr<Chassis> chassis = 123 std::make_unique<Chassis>(1, std::move(devices)); 124 Chassis* chassisPtr = chassis.get(); 125 126 // Create System that contains Chassis 127 std::vector<std::unique_ptr<Rule>> rules{}; 128 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 129 chassisVec.emplace_back(std::move(chassis)); 130 System system{std::move(rules), std::move(chassisVec)}; 131 132 // Execute Configuration 133 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 134 } 135 136 // Test where works: Volts value specified 137 { 138 // Create mock services. Expect logDebug() to be called. 139 MockServices services{}; 140 MockJournal& journal = services.getMockJournal(); 141 EXPECT_CALL(journal, logDebug("Configuring vdd_reg: volts=1.300000")) 142 .Times(1); 143 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 144 145 // Create PMBusWriteVoutCommandAction. Do not specify a volts value 146 // because it will get a value of 1.3V from the 147 // ActionEnvironment/Configuration. Specify a -8 exponent. 148 // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D. 149 std::optional<double> volts{}; 150 std::unique_ptr<PMBusWriteVoutCommandAction> action = 151 std::make_unique<PMBusWriteVoutCommandAction>( 152 volts, pmbus_utils::VoutDataFormat::linear, -8, false); 153 154 // Create mock I2CInterface. Expect action to write 0x014D to 155 // VOUT_COMMAND (command/register 0x21). 156 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 157 std::make_unique<i2c::MockedI2CInterface>(); 158 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 159 EXPECT_CALL(*i2cInterface, 160 write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D))) 161 .Times(1); 162 163 // Create Configuration with volts value 1.3V 164 std::vector<std::unique_ptr<Action>> actions{}; 165 actions.emplace_back(std::move(action)); 166 std::unique_ptr<Configuration> configuration = 167 std::make_unique<Configuration>(1.3, std::move(actions)); 168 Configuration* configurationPtr = configuration.get(); 169 170 // Create Device that contains Configuration 171 std::unique_ptr<PresenceDetection> presenceDetection{}; 172 std::unique_ptr<Device> device = std::make_unique<Device>( 173 "vdd_reg", true, 174 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 175 std::move(i2cInterface), std::move(presenceDetection), 176 std::move(configuration)); 177 Device* devicePtr = device.get(); 178 179 // Create Chassis that contains Device 180 std::vector<std::unique_ptr<Device>> devices{}; 181 devices.emplace_back(std::move(device)); 182 std::unique_ptr<Chassis> chassis = 183 std::make_unique<Chassis>(1, std::move(devices)); 184 Chassis* chassisPtr = chassis.get(); 185 186 // Create System that contains Chassis 187 std::vector<std::unique_ptr<Rule>> rules{}; 188 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 189 chassisVec.emplace_back(std::move(chassis)); 190 System system{std::move(rules), std::move(chassisVec)}; 191 192 // Execute Configuration 193 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 194 } 195 196 // Test where fails 197 { 198 // Create mock services. Expect logDebug() and logError() to be called. 199 MockServices services{}; 200 MockJournal& journal = services.getMockJournal(); 201 std::vector<std::string> expectedErrMessagesException{ 202 "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70", 203 "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: " 204 "0xFF }"}; 205 EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1); 206 EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1); 207 EXPECT_CALL(journal, logError("Unable to configure vdd_reg")).Times(1); 208 209 // Create I2CWriteByteAction with register 0x7C and value 0x0A 210 std::unique_ptr<I2CWriteByteAction> action = 211 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 212 213 // Create mock I2CInterface. write() throws an I2CException. 214 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 215 std::make_unique<i2c::MockedI2CInterface>(); 216 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 217 EXPECT_CALL(*i2cInterface, 218 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 219 .Times(1) 220 .WillOnce(Throw( 221 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70})); 222 223 // Create Configuration with no volts value specified 224 std::optional<double> volts{}; 225 std::vector<std::unique_ptr<Action>> actions{}; 226 actions.emplace_back(std::move(action)); 227 std::unique_ptr<Configuration> configuration = 228 std::make_unique<Configuration>(volts, std::move(actions)); 229 Configuration* configurationPtr = configuration.get(); 230 231 // Create Device that contains Configuration 232 std::unique_ptr<PresenceDetection> presenceDetection{}; 233 std::unique_ptr<Device> device = std::make_unique<Device>( 234 "vdd_reg", true, 235 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 236 std::move(i2cInterface), std::move(presenceDetection), 237 std::move(configuration)); 238 Device* devicePtr = device.get(); 239 240 // Create Chassis that contains Device 241 std::vector<std::unique_ptr<Device>> devices{}; 242 devices.emplace_back(std::move(device)); 243 std::unique_ptr<Chassis> chassis = 244 std::make_unique<Chassis>(1, std::move(devices)); 245 Chassis* chassisPtr = chassis.get(); 246 247 // Create System that contains Chassis 248 std::vector<std::unique_ptr<Rule>> rules{}; 249 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 250 chassisVec.emplace_back(std::move(chassis)); 251 System system{std::move(rules), std::move(chassisVec)}; 252 253 // Execute Configuration 254 configurationPtr->execute(services, system, *chassisPtr, *devicePtr); 255 } 256 } 257 258 // Test for execute(Services&, System&, Chassis&, Device&, Rail&) 259 TEST(ConfigurationTests, ExecuteForRail) 260 { 261 // Test where works: Volts value not specified 262 { 263 // Create mock services. Expect logDebug() to be called. 264 MockServices services{}; 265 MockJournal& journal = services.getMockJournal(); 266 EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1); 267 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 268 269 // Create I2CWriteByteAction with register 0x7C and value 0x0A 270 std::unique_ptr<I2CWriteByteAction> action = 271 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 272 273 // Create mock I2CInterface. Expect action to write 0x0A to 0x7C. 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, 278 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 279 .Times(1); 280 281 // Create Configuration with no volts value specified 282 std::optional<double> volts{}; 283 std::vector<std::unique_ptr<Action>> actions{}; 284 actions.emplace_back(std::move(action)); 285 std::unique_ptr<Configuration> configuration = 286 std::make_unique<Configuration>(volts, std::move(actions)); 287 Configuration* configurationPtr = configuration.get(); 288 289 // Create Rail that contains Configuration 290 std::unique_ptr<Rail> rail = 291 std::make_unique<Rail>("vio2", std::move(configuration)); 292 Rail* railPtr = rail.get(); 293 294 // Create Device that contains Rail 295 std::unique_ptr<PresenceDetection> presenceDetection{}; 296 std::unique_ptr<Configuration> deviceConfiguration{}; 297 std::vector<std::unique_ptr<Rail>> rails{}; 298 rails.emplace_back(std::move(rail)); 299 std::unique_ptr<Device> device = std::make_unique<Device>( 300 "reg1", true, 301 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 302 std::move(i2cInterface), std::move(presenceDetection), 303 std::move(deviceConfiguration), std::move(rails)); 304 Device* devicePtr = device.get(); 305 306 // Create Chassis that contains Device 307 std::vector<std::unique_ptr<Device>> devices{}; 308 devices.emplace_back(std::move(device)); 309 std::unique_ptr<Chassis> chassis = 310 std::make_unique<Chassis>(1, std::move(devices)); 311 Chassis* chassisPtr = chassis.get(); 312 313 // Create System that contains Chassis 314 std::vector<std::unique_ptr<Rule>> rules{}; 315 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 316 chassisVec.emplace_back(std::move(chassis)); 317 System system{std::move(rules), std::move(chassisVec)}; 318 319 // Execute Configuration 320 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 321 *railPtr); 322 } 323 324 // Test where works: Volts value specified 325 { 326 // Create mock services. Expect logDebug() to be called. 327 MockServices services{}; 328 MockJournal& journal = services.getMockJournal(); 329 EXPECT_CALL(journal, logDebug("Configuring vio2: volts=1.300000")) 330 .Times(1); 331 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0); 332 333 // Create PMBusWriteVoutCommandAction. Do not specify a volts value 334 // because it will get a value of 1.3V from the 335 // ActionEnvironment/Configuration. Specify a -8 exponent. 336 // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D. 337 std::optional<double> volts{}; 338 std::unique_ptr<PMBusWriteVoutCommandAction> action = 339 std::make_unique<PMBusWriteVoutCommandAction>( 340 volts, pmbus_utils::VoutDataFormat::linear, -8, false); 341 342 // Create mock I2CInterface. Expect action to write 0x014D to 343 // VOUT_COMMAND (command/register 0x21). 344 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 345 std::make_unique<i2c::MockedI2CInterface>(); 346 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 347 EXPECT_CALL(*i2cInterface, 348 write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D))) 349 .Times(1); 350 351 // Create Configuration with volts value 1.3V 352 std::vector<std::unique_ptr<Action>> actions{}; 353 actions.emplace_back(std::move(action)); 354 std::unique_ptr<Configuration> configuration = 355 std::make_unique<Configuration>(1.3, std::move(actions)); 356 Configuration* configurationPtr = configuration.get(); 357 358 // Create Rail that contains Configuration 359 std::unique_ptr<Rail> rail = 360 std::make_unique<Rail>("vio2", std::move(configuration)); 361 Rail* railPtr = rail.get(); 362 363 // Create Device that contains Rail 364 std::unique_ptr<PresenceDetection> presenceDetection{}; 365 std::unique_ptr<Configuration> deviceConfiguration{}; 366 std::vector<std::unique_ptr<Rail>> rails{}; 367 rails.emplace_back(std::move(rail)); 368 std::unique_ptr<Device> device = std::make_unique<Device>( 369 "reg1", true, 370 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 371 std::move(i2cInterface), std::move(presenceDetection), 372 std::move(deviceConfiguration), std::move(rails)); 373 Device* devicePtr = device.get(); 374 375 // Create Chassis that contains Device 376 std::vector<std::unique_ptr<Device>> devices{}; 377 devices.emplace_back(std::move(device)); 378 std::unique_ptr<Chassis> chassis = 379 std::make_unique<Chassis>(1, std::move(devices)); 380 Chassis* chassisPtr = chassis.get(); 381 382 // Create System that contains Chassis 383 std::vector<std::unique_ptr<Rule>> rules{}; 384 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 385 chassisVec.emplace_back(std::move(chassis)); 386 System system{std::move(rules), std::move(chassisVec)}; 387 388 // Execute Configuration 389 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 390 *railPtr); 391 } 392 393 // Test where fails 394 { 395 // Create mock services. Expect logDebug() and logError() to be called. 396 MockServices services{}; 397 MockJournal& journal = services.getMockJournal(); 398 std::vector<std::string> expectedErrMessagesException{ 399 "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70", 400 "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: " 401 "0xFF }"}; 402 EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1); 403 EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1); 404 EXPECT_CALL(journal, logError("Unable to configure vio2")).Times(1); 405 406 // Create I2CWriteByteAction with register 0x7C and value 0x0A 407 std::unique_ptr<I2CWriteByteAction> action = 408 std::make_unique<I2CWriteByteAction>(0x7C, 0x0A); 409 410 // Create mock I2CInterface. write() throws an I2CException. 411 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 412 std::make_unique<i2c::MockedI2CInterface>(); 413 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 414 EXPECT_CALL(*i2cInterface, 415 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A))) 416 .Times(1) 417 .WillOnce(Throw( 418 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70})); 419 420 // Create Configuration with no volts value specified 421 std::optional<double> volts{}; 422 std::vector<std::unique_ptr<Action>> actions{}; 423 actions.emplace_back(std::move(action)); 424 std::unique_ptr<Configuration> configuration = 425 std::make_unique<Configuration>(volts, std::move(actions)); 426 Configuration* configurationPtr = configuration.get(); 427 428 // Create Rail that contains Configuration 429 std::unique_ptr<Rail> rail = 430 std::make_unique<Rail>("vio2", std::move(configuration)); 431 Rail* railPtr = rail.get(); 432 433 // Create Device that contains Rail 434 std::unique_ptr<PresenceDetection> presenceDetection{}; 435 std::unique_ptr<Configuration> deviceConfiguration{}; 436 std::vector<std::unique_ptr<Rail>> rails{}; 437 rails.emplace_back(std::move(rail)); 438 std::unique_ptr<Device> device = std::make_unique<Device>( 439 "reg1", true, 440 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", 441 std::move(i2cInterface), std::move(presenceDetection), 442 std::move(deviceConfiguration), std::move(rails)); 443 Device* devicePtr = device.get(); 444 445 // Create Chassis that contains Device 446 std::vector<std::unique_ptr<Device>> devices{}; 447 devices.emplace_back(std::move(device)); 448 std::unique_ptr<Chassis> chassis = 449 std::make_unique<Chassis>(1, std::move(devices)); 450 Chassis* chassisPtr = chassis.get(); 451 452 // Create System that contains Chassis 453 std::vector<std::unique_ptr<Rule>> rules{}; 454 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 455 chassisVec.emplace_back(std::move(chassis)); 456 System system{std::move(rules), std::move(chassisVec)}; 457 458 // Execute Configuration 459 configurationPtr->execute(services, system, *chassisPtr, *devicePtr, 460 *railPtr); 461 } 462 } 463 464 TEST(ConfigurationTests, GetActions) 465 { 466 std::optional<double> volts{1.3}; 467 468 std::vector<std::unique_ptr<Action>> actions{}; 469 470 MockAction* action1 = new MockAction{}; 471 actions.push_back(std::unique_ptr<MockAction>{action1}); 472 473 MockAction* action2 = new MockAction{}; 474 actions.push_back(std::unique_ptr<MockAction>{action2}); 475 476 Configuration configuration(volts, std::move(actions)); 477 EXPECT_EQ(configuration.getActions().size(), 2); 478 EXPECT_EQ(configuration.getActions()[0].get(), action1); 479 EXPECT_EQ(configuration.getActions()[1].get(), action2); 480 } 481 482 TEST(ConfigurationTests, GetVolts) 483 { 484 // Test where volts value specified 485 { 486 std::optional<double> volts{3.2}; 487 488 std::vector<std::unique_ptr<Action>> actions{}; 489 actions.push_back(std::make_unique<MockAction>()); 490 491 Configuration configuration(volts, std::move(actions)); 492 EXPECT_EQ(configuration.getVolts().has_value(), true); 493 EXPECT_EQ(configuration.getVolts().value(), 3.2); 494 } 495 496 // Test where volts value not specified 497 { 498 std::optional<double> volts{}; 499 500 std::vector<std::unique_ptr<Action>> actions{}; 501 actions.push_back(std::make_unique<MockAction>()); 502 503 Configuration configuration(volts, std::move(actions)); 504 EXPECT_EQ(configuration.getVolts().has_value(), false); 505 } 506 } 507