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