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
TEST(I2CCaptureBytesActionTests,Constructor)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
TEST(I2CCaptureBytesActionTests,Execute)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
TEST(I2CCaptureBytesActionTests,GetCount)265 TEST(I2CCaptureBytesActionTests, GetCount)
266 {
267 I2CCaptureBytesAction action{0xA0, 3};
268 EXPECT_EQ(action.getCount(), 3);
269 }
270
TEST(I2CCaptureBytesActionTests,GetRegister)271 TEST(I2CCaptureBytesActionTests, GetRegister)
272 {
273 I2CCaptureBytesAction action{0xA0, 3};
274 EXPECT_EQ(action.getRegister(), 0xA0);
275 }
276
TEST(I2CCaptureBytesActionTests,ToString)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