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_environment.hpp"
17 #include "action_error.hpp"
18 #include "device.hpp"
19 #include "i2c_interface.hpp"
20 #include "i2c_write_bit_action.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::A;
37 using ::testing::Return;
38 using ::testing::SetArgReferee;
39 using ::testing::Throw;
40 using ::testing::TypedEq;
41
TEST(I2CWriteBitActionTests,Constructor)42 TEST(I2CWriteBitActionTests, Constructor)
43 {
44 // Test where works
45 try
46 {
47 I2CWriteBitAction action{0x7C, 2, 0};
48 EXPECT_EQ(action.getRegister(), 0x7C);
49 EXPECT_EQ(action.getPosition(), 2);
50 EXPECT_EQ(action.getValue(), 0);
51 }
52 catch (...)
53 {
54 ADD_FAILURE() << "Should not have caught exception.";
55 }
56
57 // Test where fails: Invalid bit position > 7
58 try
59 {
60 I2CWriteBitAction action{0x7C, 8, 0};
61 ADD_FAILURE() << "Should not have reached this line.";
62 }
63 catch (const std::invalid_argument& e)
64 {
65 EXPECT_STREQ(e.what(), "Invalid bit position: 8");
66 }
67 catch (...)
68 {
69 ADD_FAILURE() << "Should not have caught exception.";
70 }
71
72 // Test where fails: Invalid bit value > 1
73 try
74 {
75 I2CWriteBitAction action{0x7C, 2, 2};
76 ADD_FAILURE() << "Should not have reached this line.";
77 }
78 catch (const std::invalid_argument& e)
79 {
80 EXPECT_STREQ(e.what(), "Invalid bit value: 2");
81 }
82 catch (...)
83 {
84 ADD_FAILURE() << "Should not have caught exception.";
85 }
86 }
87
TEST(I2CWriteBitActionTests,Execute)88 TEST(I2CWriteBitActionTests, Execute)
89 {
90 // Test where works: Value is 0
91 try
92 {
93 // Create mock I2CInterface: read() returns value 0xB6
94 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
95 std::make_unique<i2c::MockedI2CInterface>();
96 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
97 EXPECT_CALL(*i2cInterface, read(0xA0, A<uint8_t&>()))
98 .Times(1)
99 .WillOnce(SetArgReferee<1>(0xB6));
100 EXPECT_CALL(*i2cInterface,
101 write(TypedEq<uint8_t>(0xA0), TypedEq<uint8_t>(0x96)))
102 .Times(1);
103
104 // Create Device, IDMap, MockServices, and ActionEnvironment
105 Device device{
106 "reg1", true,
107 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
108 std::move(i2cInterface)};
109 IDMap idMap{};
110 idMap.addDevice(device);
111 MockServices services{};
112 ActionEnvironment env{idMap, "reg1", services};
113
114 // Register value : 0xB6 = 1011 0110
115 // 0 in position 5 : 0x00 = --0- ----
116 // New register value: 0x96 = 1001 0110
117 I2CWriteBitAction action{0xA0, 5, 0};
118 EXPECT_EQ(action.execute(env), true);
119 }
120 catch (...)
121 {
122 ADD_FAILURE() << "Should not have caught exception.";
123 }
124
125 // Test where works: Value is 1
126 try
127 {
128 // Create mock I2CInterface: read() returns value 0x96
129 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
130 std::make_unique<i2c::MockedI2CInterface>();
131 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
132 EXPECT_CALL(*i2cInterface, read(0x7C, A<uint8_t&>()))
133 .Times(1)
134 .WillOnce(SetArgReferee<1>(0x96));
135 EXPECT_CALL(*i2cInterface,
136 write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0xB6)))
137 .Times(1);
138
139 // Create Device, IDMap, MockServices, and ActionEnvironment
140 Device device{
141 "reg1", true,
142 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
143 std::move(i2cInterface)};
144 IDMap idMap{};
145 idMap.addDevice(device);
146 MockServices services{};
147 ActionEnvironment env{idMap, "reg1", services};
148
149 // Register value : 0x96 = 1001 0110
150 // 1 in position 5 : 0x20 = 0010 0000
151 // New register value: 0xB6 = 1011 0110
152 I2CWriteBitAction action{0x7C, 5, 1};
153 EXPECT_EQ(action.execute(env), true);
154 }
155 catch (...)
156 {
157 ADD_FAILURE() << "Should not have caught exception.";
158 }
159
160 // Test where fails: Getting I2CInterface fails
161 try
162 {
163 // Create IDMap, MockServices, and ActionEnvironment
164 IDMap idMap{};
165 MockServices services{};
166 ActionEnvironment env{idMap, "reg1", services};
167
168 I2CWriteBitAction action{0x7C, 5, 1};
169 action.execute(env);
170 ADD_FAILURE() << "Should not have reached this line.";
171 }
172 catch (const std::invalid_argument& e)
173 {
174 EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
175 }
176 catch (...)
177 {
178 ADD_FAILURE() << "Should not have caught exception.";
179 }
180
181 // Test where fails: Reading byte fails
182 try
183 {
184 // Create mock I2CInterface: read() throws an I2CException
185 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
186 std::make_unique<i2c::MockedI2CInterface>();
187 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
188 EXPECT_CALL(*i2cInterface, read(0x7C, A<uint8_t&>()))
189 .Times(1)
190 .WillOnce(Throw(
191 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
192 EXPECT_CALL(*i2cInterface, write(A<uint8_t>(), A<uint8_t>())).Times(0);
193
194 // Create Device, IDMap, MockServices and ActionEnvironment
195 Device device{
196 "reg1", true,
197 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
198 std::move(i2cInterface)};
199 IDMap idMap{};
200 idMap.addDevice(device);
201 MockServices services{};
202 ActionEnvironment env{idMap, "reg1", services};
203
204 I2CWriteBitAction action{0x7C, 5, 1};
205 action.execute(env);
206 ADD_FAILURE() << "Should not have reached this line.";
207 }
208 catch (const ActionError& e)
209 {
210 EXPECT_STREQ(e.what(), "ActionError: i2c_write_bit: { register: "
211 "0x7C, position: 5, value: 1 }");
212 try
213 {
214 // Re-throw inner I2CException
215 std::rethrow_if_nested(e);
216 ADD_FAILURE() << "Should not have reached this line.";
217 }
218 catch (const i2c::I2CException& ie)
219 {
220 EXPECT_STREQ(
221 ie.what(),
222 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70");
223 }
224 catch (...)
225 {
226 ADD_FAILURE() << "Should not have caught exception.";
227 }
228 }
229 catch (...)
230 {
231 ADD_FAILURE() << "Should not have caught exception.";
232 }
233
234 // Test where fails: Writing byte fails
235 try
236 {
237 // Create mock I2CInterface: read() returns value 0xB6, write() throws
238 // an I2CException
239 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
240 std::make_unique<i2c::MockedI2CInterface>();
241 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
242 EXPECT_CALL(*i2cInterface, read(0xA0, A<uint8_t&>()))
243 .Times(1)
244 .WillOnce(SetArgReferee<1>(0xB6));
245 EXPECT_CALL(*i2cInterface,
246 write(TypedEq<uint8_t>(0xA0), TypedEq<uint8_t>(0x96)))
247 .Times(1)
248 .WillOnce(Throw(
249 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70}));
250
251 // Create Device, IDMap, MockServices, and ActionEnvironment
252 Device device{
253 "reg1", true,
254 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
255 std::move(i2cInterface)};
256 IDMap idMap{};
257 idMap.addDevice(device);
258 MockServices services{};
259 ActionEnvironment env{idMap, "reg1", services};
260
261 // Register value : 0xB6 = 1011 0110
262 // 0 in position 5 : 0x00 = --0- ----
263 // New register value: 0x96 = 1001 0110
264 I2CWriteBitAction action{0xA0, 5, 0};
265 action.execute(env);
266 ADD_FAILURE() << "Should not have reached this line.";
267 }
268 catch (const ActionError& e)
269 {
270 EXPECT_STREQ(e.what(), "ActionError: i2c_write_bit: { register: "
271 "0xA0, position: 5, value: 0 }");
272 try
273 {
274 // Re-throw inner I2CException
275 std::rethrow_if_nested(e);
276 ADD_FAILURE() << "Should not have reached this line.";
277 }
278 catch (const i2c::I2CException& ie)
279 {
280 EXPECT_STREQ(ie.what(), "I2CException: Failed to write byte: bus "
281 "/dev/i2c-1, addr 0x70");
282 }
283 catch (...)
284 {
285 ADD_FAILURE() << "Should not have caught exception.";
286 }
287 }
288 catch (...)
289 {
290 ADD_FAILURE() << "Should not have caught exception.";
291 }
292 }
293
TEST(I2CWriteBitActionTests,GetRegister)294 TEST(I2CWriteBitActionTests, GetRegister)
295 {
296 I2CWriteBitAction action{0x7C, 5, 1};
297 EXPECT_EQ(action.getRegister(), 0x7C);
298 }
299
TEST(I2CWriteBitActionTests,GetPosition)300 TEST(I2CWriteBitActionTests, GetPosition)
301 {
302 I2CWriteBitAction action{0x7C, 5, 1};
303 EXPECT_EQ(action.getPosition(), 5);
304 }
305
TEST(I2CWriteBitActionTests,GetValue)306 TEST(I2CWriteBitActionTests, GetValue)
307 {
308 I2CWriteBitAction action{0x7C, 5, 1};
309 EXPECT_EQ(action.getValue(), 1);
310 }
311
TEST(I2CWriteBitActionTests,ToString)312 TEST(I2CWriteBitActionTests, ToString)
313 {
314 I2CWriteBitAction action{0x7C, 5, 1};
315 EXPECT_EQ(action.toString(),
316 "i2c_write_bit: { register: 0x7C, position: 5, value: 1 }");
317 }
318