1 /** 2 * Copyright © 2021 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_environment.hpp" 17 #include "action_error.hpp" 18 #include "device.hpp" 19 #include "i2c_capture_bytes_action.hpp" 20 #include "i2c_interface.hpp" 21 #include "id_map.hpp" 22 #include "mock_services.hpp" 23 #include "mocked_i2c_interface.hpp" 24 25 #include <cstdint> 26 #include <memory> 27 #include <stdexcept> 28 #include <string> 29 #include <utility> 30 31 #include <gmock/gmock.h> 32 #include <gtest/gtest.h> 33 34 using namespace phosphor::power::regulators; 35 36 using ::testing::NotNull; 37 using ::testing::Return; 38 using ::testing::SetArrayArgument; 39 using ::testing::Throw; 40 using ::testing::TypedEq; 41 42 TEST(I2CCaptureBytesActionTests, Constructor) 43 { 44 // Test where works 45 try 46 { 47 I2CCaptureBytesAction action{0x2A, 2}; 48 EXPECT_EQ(action.getRegister(), 0x2A); 49 EXPECT_EQ(action.getCount(), 2); 50 } 51 catch (...) 52 { 53 ADD_FAILURE() << "Should not have caught exception."; 54 } 55 56 // Test where fails: Count < 1 57 try 58 { 59 I2CCaptureBytesAction action{0x2A, 0}; 60 ADD_FAILURE() << "Should not have reached this line."; 61 } 62 catch (const std::invalid_argument& e) 63 { 64 EXPECT_STREQ(e.what(), "Invalid byte count: Less than 1"); 65 } 66 catch (...) 67 { 68 ADD_FAILURE() << "Should not have caught exception."; 69 } 70 } 71 72 TEST(I2CCaptureBytesActionTests, Execute) 73 { 74 // Test where works: One byte captured 75 try 76 { 77 // Create mock I2CInterface: read() returns value 0xD7 78 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 79 std::make_unique<i2c::MockedI2CInterface>(); 80 uint8_t values[] = {0xD7}; 81 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 82 EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(1), NotNull(), 83 i2c::I2CInterface::Mode::I2C)) 84 .Times(1) 85 .WillOnce(SetArrayArgument<2>(values, values + 1)); 86 87 // Create Device, IDMap, MockServices, and ActionEnvironment 88 Device device{ 89 "vdd1", true, 90 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1", 91 std::move(i2cInterface)}; 92 IDMap idMap{}; 93 idMap.addDevice(device); 94 MockServices services{}; 95 ActionEnvironment env{idMap, "vdd1", services}; 96 97 I2CCaptureBytesAction action{0xA0, 1}; 98 EXPECT_EQ(action.execute(env), true); 99 EXPECT_EQ(env.getAdditionalErrorData().size(), 1); 100 EXPECT_EQ(env.getAdditionalErrorData().at("vdd1_register_0xA0"), 101 "[ 0xD7 ]"); 102 } 103 catch (...) 104 { 105 ADD_FAILURE() << "Should not have caught exception."; 106 } 107 108 // Test where works: Multiple bytes captured 109 try 110 { 111 // Create mock I2CInterface: read() returns values 0x56, 0x14, 0xDA 112 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 113 std::make_unique<i2c::MockedI2CInterface>(); 114 uint8_t values[] = {0x56, 0x14, 0xDA}; 115 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 116 EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq<uint8_t&>(3), NotNull(), 117 i2c::I2CInterface::Mode::I2C)) 118 .Times(1) 119 .WillOnce(SetArrayArgument<2>(values, values + 3)); 120 121 // Create Device, IDMap, MockServices, and ActionEnvironment 122 Device device{ 123 "vdd1", true, 124 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1", 125 std::move(i2cInterface)}; 126 IDMap idMap{}; 127 idMap.addDevice(device); 128 MockServices services{}; 129 ActionEnvironment env{idMap, "vdd1", services}; 130 131 I2CCaptureBytesAction action{0x7C, 3}; 132 EXPECT_EQ(action.execute(env), true); 133 EXPECT_EQ(env.getAdditionalErrorData().size(), 1); 134 EXPECT_EQ(env.getAdditionalErrorData().at("vdd1_register_0x7C"), 135 "[ 0x56, 0x14, 0xDA ]"); 136 } 137 catch (...) 138 { 139 ADD_FAILURE() << "Should not have caught exception."; 140 } 141 142 // Test where works: Same device + register captured multiple times 143 try 144 { 145 // Create mock I2CInterface: read() will be called 3 times and will 146 // return values 0xD7, 0x13, and 0xFB 147 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 148 std::make_unique<i2c::MockedI2CInterface>(); 149 uint8_t values_1[] = {0xD7}; 150 uint8_t values_2[] = {0x13}; 151 uint8_t values_3[] = {0xFB}; 152 EXPECT_CALL(*i2cInterface, isOpen) 153 .Times(3) 154 .WillRepeatedly(Return(true)); 155 EXPECT_CALL(*i2cInterface, read(0xCA, TypedEq<uint8_t&>(1), NotNull(), 156 i2c::I2CInterface::Mode::I2C)) 157 .Times(3) 158 .WillOnce(SetArrayArgument<2>(values_1, values_1 + 1)) 159 .WillOnce(SetArrayArgument<2>(values_2, values_2 + 1)) 160 .WillOnce(SetArrayArgument<2>(values_3, values_3 + 1)); 161 162 // Create Device, IDMap, MockServices, and ActionEnvironment 163 Device device{ 164 "vdd1", true, 165 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1", 166 std::move(i2cInterface)}; 167 IDMap idMap{}; 168 idMap.addDevice(device); 169 MockServices services{}; 170 ActionEnvironment env{idMap, "vdd1", services}; 171 172 I2CCaptureBytesAction action{0xCA, 1}; 173 EXPECT_EQ(action.execute(env), true); 174 EXPECT_EQ(action.execute(env), true); 175 EXPECT_EQ(action.execute(env), true); 176 EXPECT_EQ(env.getAdditionalErrorData().size(), 3); 177 EXPECT_EQ(env.getAdditionalErrorData().at("vdd1_register_0xCA"), 178 "[ 0xD7 ]"); 179 EXPECT_EQ(env.getAdditionalErrorData().at("vdd1_register_0xCA_2"), 180 "[ 0x13 ]"); 181 EXPECT_EQ(env.getAdditionalErrorData().at("vdd1_register_0xCA_3"), 182 "[ 0xFB ]"); 183 } 184 catch (...) 185 { 186 ADD_FAILURE() << "Should not have caught exception."; 187 } 188 189 // Test where fails: Getting I2CInterface fails 190 try 191 { 192 // Create IDMap, MockServices, and ActionEnvironment 193 IDMap idMap{}; 194 MockServices services{}; 195 ActionEnvironment env{idMap, "vdd1", services}; 196 197 I2CCaptureBytesAction action{0x7C, 2}; 198 action.execute(env); 199 ADD_FAILURE() << "Should not have reached this line."; 200 } 201 catch (const std::invalid_argument& e) 202 { 203 EXPECT_STREQ(e.what(), "Unable to find device with ID \"vdd1\""); 204 } 205 catch (...) 206 { 207 ADD_FAILURE() << "Should not have caught exception."; 208 } 209 210 // Test where fails: Reading bytes fails 211 try 212 { 213 // Create mock I2CInterface: read() 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, read(0x7C, TypedEq<uint8_t&>(2), NotNull(), 218 i2c::I2CInterface::Mode::I2C)) 219 .Times(1) 220 .WillOnce(Throw(i2c::I2CException{"Failed to read i2c block data", 221 "/dev/i2c-1", 0x70})); 222 223 // Create Device, IDMap, MockServices, and ActionEnvironment 224 Device device{ 225 "vdd1", true, 226 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1", 227 std::move(i2cInterface)}; 228 IDMap idMap{}; 229 idMap.addDevice(device); 230 MockServices services{}; 231 ActionEnvironment env{idMap, "vdd1", services}; 232 233 I2CCaptureBytesAction action{0x7C, 2}; 234 action.execute(env); 235 ADD_FAILURE() << "Should not have reached this line."; 236 } 237 catch (const ActionError& e) 238 { 239 EXPECT_STREQ( 240 e.what(), 241 "ActionError: i2c_capture_bytes: { register: 0x7C, count: 2 }"); 242 try 243 { 244 // Re-throw inner I2CException 245 std::rethrow_if_nested(e); 246 ADD_FAILURE() << "Should not have reached this line."; 247 } 248 catch (const i2c::I2CException& ie) 249 { 250 EXPECT_STREQ(ie.what(), 251 "I2CException: Failed to read i2c block data: bus " 252 "/dev/i2c-1, addr 0x70"); 253 } 254 catch (...) 255 { 256 ADD_FAILURE() << "Should not have caught exception."; 257 } 258 } 259 catch (...) 260 { 261 ADD_FAILURE() << "Should not have caught exception."; 262 } 263 } 264 265 TEST(I2CCaptureBytesActionTests, GetCount) 266 { 267 I2CCaptureBytesAction action{0xA0, 3}; 268 EXPECT_EQ(action.getCount(), 3); 269 } 270 271 TEST(I2CCaptureBytesActionTests, GetRegister) 272 { 273 I2CCaptureBytesAction action{0xA0, 3}; 274 EXPECT_EQ(action.getRegister(), 0xA0); 275 } 276 277 TEST(I2CCaptureBytesActionTests, ToString) 278 { 279 I2CCaptureBytesAction action{0xA0, 3}; 280 EXPECT_EQ(action.toString(), 281 "i2c_capture_bytes: { register: 0xA0, count: 3 }"); 282 } 283