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_bytes_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 #include <vector>
31
32 #include <gmock/gmock.h>
33 #include <gtest/gtest.h>
34
35 using namespace phosphor::power::regulators;
36
37 using ::testing::A;
38 using ::testing::Args;
39 using ::testing::ElementsAre;
40 using ::testing::NotNull;
41 using ::testing::Return;
42 using ::testing::SetArrayArgument;
43 using ::testing::Throw;
44 using ::testing::TypedEq;
45
46 // Test for I2CWriteBytesAction(uint8_t reg,
47 // const std::vector<uint8_t>& values)
TEST(I2CWriteBytesActionTests,Constructor1)48 TEST(I2CWriteBytesActionTests, Constructor1)
49 {
50 // Test where works
51 try
52 {
53 std::vector<uint8_t> values{0x56, 0x14, 0xDA};
54 I2CWriteBytesAction action{0x7C, values};
55
56 EXPECT_EQ(action.getRegister(), 0x7C);
57
58 EXPECT_EQ(action.getValues().size(), 3);
59 EXPECT_EQ(action.getValues()[0], 0x56);
60 EXPECT_EQ(action.getValues()[1], 0x14);
61 EXPECT_EQ(action.getValues()[2], 0xDA);
62
63 EXPECT_EQ(action.getMasks().size(), 0);
64 }
65 catch (...)
66 {
67 ADD_FAILURE() << "Should not have caught exception.";
68 }
69
70 // Test where fails: Values vector is empty
71 try
72 {
73 std::vector<uint8_t> values{};
74 I2CWriteBytesAction action{0x7C, values};
75 ADD_FAILURE() << "Should not have reached this line.";
76 }
77 catch (const std::invalid_argument& e)
78 {
79 EXPECT_STREQ(e.what(), "Values vector is empty");
80 }
81 catch (...)
82 {
83 ADD_FAILURE() << "Should not have caught exception.";
84 }
85 }
86
87 // Test for I2CWriteBytesAction(uint8_t reg,
88 // const std::vector<uint8_t>& values,
89 // const std::vector<uint8_t>& masks)
TEST(I2CWriteBytesActionTests,Constructor2)90 TEST(I2CWriteBytesActionTests, Constructor2)
91 {
92 // Test where works
93 try
94 {
95 std::vector<uint8_t> values{0x56, 0x14};
96 std::vector<uint8_t> masks{0x7E, 0x3C};
97 I2CWriteBytesAction action{0xA0, values, masks};
98
99 EXPECT_EQ(action.getRegister(), 0xA0);
100
101 EXPECT_EQ(action.getValues().size(), 2);
102 EXPECT_EQ(action.getValues()[0], 0x56);
103 EXPECT_EQ(action.getValues()[1], 0x14);
104
105 EXPECT_EQ(action.getMasks().size(), 2);
106 EXPECT_EQ(action.getMasks()[0], 0x7E);
107 EXPECT_EQ(action.getMasks()[1], 0x3C);
108 }
109 catch (...)
110 {
111 ADD_FAILURE() << "Should not have caught exception.";
112 }
113
114 // Test where fails: Values vector is empty
115 try
116 {
117 std::vector<uint8_t> values{};
118 std::vector<uint8_t> masks{};
119 I2CWriteBytesAction action{0xA0, values, masks};
120 ADD_FAILURE() << "Should not have reached this line.";
121 }
122 catch (const std::invalid_argument& e)
123 {
124 EXPECT_STREQ(e.what(), "Values vector is empty");
125 }
126 catch (...)
127 {
128 ADD_FAILURE() << "Should not have caught exception.";
129 }
130
131 // Test where fails: Masks vector different size than values vector
132 try
133 {
134 std::vector<uint8_t> values{0x56, 0x14, 0xFE};
135 std::vector<uint8_t> masks{0x7E, 0x3C};
136 I2CWriteBytesAction action{0x7C, values, masks};
137 ADD_FAILURE() << "Should not have reached this line.";
138 }
139 catch (const std::invalid_argument& e)
140 {
141 EXPECT_STREQ(e.what(), "Masks vector has invalid size");
142 }
143 catch (...)
144 {
145 ADD_FAILURE() << "Should not have caught exception.";
146 }
147 }
148
TEST(I2CWriteBytesActionTests,Execute)149 TEST(I2CWriteBytesActionTests, Execute)
150 {
151 // Test where works: Masks not specified
152 try
153 {
154 // Create mock I2CInterface
155 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
156 std::make_unique<i2c::MockedI2CInterface>();
157 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
158 EXPECT_CALL(*i2cInterface,
159 read(A<uint8_t>(), A<uint8_t&>(), A<uint8_t*>(),
160 A<i2c::I2CInterface::Mode>()))
161 .Times(0);
162 EXPECT_CALL(*i2cInterface,
163 write(0x7C, 3, NotNull(), i2c::I2CInterface::Mode::I2C))
164 .With(Args<2, 1>(ElementsAre(0x56, 0x14, 0xDA)))
165 .Times(1);
166
167 // Create Device, IDMap, MockServices, and ActionEnvironment
168 Device device{
169 "reg1", true,
170 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
171 std::move(i2cInterface)};
172 IDMap idMap{};
173 idMap.addDevice(device);
174 MockServices services{};
175 ActionEnvironment env{idMap, "reg1", services};
176
177 std::vector<uint8_t> values{0x56, 0x14, 0xDA};
178 I2CWriteBytesAction action{0x7C, values};
179 EXPECT_EQ(action.execute(env), true);
180 }
181 catch (...)
182 {
183 ADD_FAILURE() << "Should not have caught exception.";
184 }
185
186 // Test where works: Masks specified
187 try
188 {
189 // Create mock I2CInterface: read() returns values 0x69, 0xA5
190 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
191 std::make_unique<i2c::MockedI2CInterface>();
192 uint8_t actualValues[] = {0x69, 0xA5};
193 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
194 EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(2), NotNull(),
195 i2c::I2CInterface::Mode::I2C))
196 .Times(1)
197 .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 2));
198 EXPECT_CALL(*i2cInterface,
199 write(0xA0, 2, NotNull(), i2c::I2CInterface::Mode::I2C))
200 .With(Args<2, 1>(ElementsAre(0xEA, 0xB3)))
201 .Times(1);
202
203 // Create Device, IDMap, MockServices, and ActionEnvironment
204 Device device{
205 "reg1", true,
206 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
207 std::move(i2cInterface)};
208 IDMap idMap{};
209 idMap.addDevice(device);
210 MockServices services{};
211 ActionEnvironment env{idMap, "reg1", services};
212
213 // Byte 1 Byte 2
214 // Value to write : 0xD6 = 1101 0110 : 0xD2 = 1101 0010
215 // Mask : 0xC3 = 1100 0011 : 0x96 = 1001 0110
216 // Current value : 0x69 = 0110 1001 : 0xA5 = 1010 0101
217 // Value to write & mask: 0xC2 = 1100 0010 : 0x92 = 1001 0010
218 // ~Mask : 0x3C = 0011 1100 : 0x69 = 0110 1001
219 // Current value & ~mask: 0x28 = 0010 1000 : 0x21 = 0010 0001
220 // Final value to write : 0xEA = 1110 1010 : 0xB3 = 1011 0011
221 std::vector<uint8_t> values{0xD6, 0xD2};
222 std::vector<uint8_t> masks{0xC3, 0x96};
223 I2CWriteBytesAction action{0xA0, values, masks};
224 EXPECT_EQ(action.execute(env), true);
225 }
226 catch (...)
227 {
228 ADD_FAILURE() << "Should not have caught exception.";
229 }
230
231 // Test where works: Single byte
232 try
233 {
234 // Create mock I2CInterface: read() returns value 0x69
235 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
236 std::make_unique<i2c::MockedI2CInterface>();
237 uint8_t actualValues[] = {0x69};
238 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
239 EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(1), NotNull(),
240 i2c::I2CInterface::Mode::I2C))
241 .Times(1)
242 .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 1));
243 EXPECT_CALL(*i2cInterface,
244 write(0xA0, 1, NotNull(), i2c::I2CInterface::Mode::I2C))
245 .With(Args<2, 1>(ElementsAre(0xEA)))
246 .Times(1);
247
248 // Create Device, IDMap, MockServices, and ActionEnvironment
249 Device device{
250 "reg1", true,
251 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
252 std::move(i2cInterface)};
253 IDMap idMap{};
254 idMap.addDevice(device);
255 MockServices services{};
256 ActionEnvironment env{idMap, "reg1", services};
257
258 // Value to write : 0xD6 = 1101 0110
259 // Mask : 0xC3 = 1100 0011
260 // Current value : 0x69 = 0110 1001
261 // Value to write & mask: 0xC2 = 1100 0010
262 // ~Mask : 0x3C = 0011 1100
263 // Current value & ~mask: 0x28 = 0010 1000
264 // Final value to write : 0xEA = 1110 1010
265 std::vector<uint8_t> values{0xD6};
266 std::vector<uint8_t> masks{0xC3};
267 I2CWriteBytesAction action{0xA0, values, masks};
268 EXPECT_EQ(action.execute(env), true);
269 }
270 catch (...)
271 {
272 ADD_FAILURE() << "Should not have caught exception.";
273 }
274
275 // Test where fails: Getting I2CInterface fails
276 try
277 {
278 // Create IDMap, MockServices, and ActionEnvironment
279 IDMap idMap{};
280 MockServices services{};
281 ActionEnvironment env{idMap, "reg1", services};
282
283 std::vector<uint8_t> values{0x56, 0x14, 0xDB};
284 I2CWriteBytesAction action{0x7C, values};
285 action.execute(env);
286 ADD_FAILURE() << "Should not have reached this line.";
287 }
288 catch (const std::invalid_argument& e)
289 {
290 EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
291 }
292 catch (...)
293 {
294 ADD_FAILURE() << "Should not have caught exception.";
295 }
296
297 // Test where fails: Reading bytes fails
298 try
299 {
300 // Create mock I2CInterface: read() throws an I2CException
301 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
302 std::make_unique<i2c::MockedI2CInterface>();
303 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
304 EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(2), NotNull(),
305 i2c::I2CInterface::Mode::I2C))
306 .Times(1)
307 .WillOnce(Throw(i2c::I2CException{"Failed to read i2c block data",
308 "/dev/i2c-1", 0x70}));
309 EXPECT_CALL(*i2cInterface,
310 write(A<uint8_t>(), A<uint8_t>(), A<const uint8_t*>(),
311 A<i2c::I2CInterface::Mode>()))
312 .Times(0);
313
314 // Create Device, IDMap, MockServices, and ActionEnvironment
315 Device device{
316 "reg1", true,
317 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
318 std::move(i2cInterface)};
319 IDMap idMap{};
320 idMap.addDevice(device);
321 MockServices services{};
322 ActionEnvironment env{idMap, "reg1", services};
323
324 std::vector<uint8_t> values{0xD6, 0xD2};
325 std::vector<uint8_t> masks{0xC3, 0x96};
326 I2CWriteBytesAction action{0xA0, values, masks};
327 action.execute(env);
328 ADD_FAILURE() << "Should not have reached this line.";
329 }
330 catch (const ActionError& e)
331 {
332 EXPECT_STREQ(e.what(),
333 "ActionError: i2c_write_bytes: { register: "
334 "0xA0, values: [ 0xD6, 0xD2 ], masks: [ 0xC3, 0x96 ] }");
335 try
336 {
337 // Re-throw inner I2CException
338 std::rethrow_if_nested(e);
339 ADD_FAILURE() << "Should not have reached this line.";
340 }
341 catch (const i2c::I2CException& ie)
342 {
343 EXPECT_STREQ(ie.what(),
344 "I2CException: Failed to read i2c block data: bus "
345 "/dev/i2c-1, addr 0x70");
346 }
347 catch (...)
348 {
349 ADD_FAILURE() << "Should not have caught exception.";
350 }
351 }
352 catch (...)
353 {
354 ADD_FAILURE() << "Should not have caught exception.";
355 }
356
357 // Test where fails: Writing bytes fails
358 try
359 {
360 // Create mock I2CInterface: write() throws an I2CException
361 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
362 std::make_unique<i2c::MockedI2CInterface>();
363 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
364 EXPECT_CALL(*i2cInterface,
365 read(A<uint8_t>(), A<uint8_t&>(), A<uint8_t*>(),
366 A<i2c::I2CInterface::Mode>()))
367 .Times(0);
368 EXPECT_CALL(*i2cInterface,
369 write(0x7C, 3, NotNull(), i2c::I2CInterface::Mode::I2C))
370 .With(Args<2, 1>(ElementsAre(0x56, 0x14, 0xDA)))
371 .Times(1)
372 .WillOnce(Throw(i2c::I2CException{"Failed to write i2c block data",
373 "/dev/i2c-1", 0x70}));
374
375 // Create Device, IDMap, MockServices, and ActionEnvironment
376 Device device{
377 "reg1", true,
378 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
379 std::move(i2cInterface)};
380 IDMap idMap{};
381 idMap.addDevice(device);
382 MockServices services{};
383 ActionEnvironment env{idMap, "reg1", services};
384
385 std::vector<uint8_t> values{0x56, 0x14, 0xDA};
386 I2CWriteBytesAction action{0x7C, values};
387 EXPECT_EQ(action.execute(env), true);
388 action.execute(env);
389 ADD_FAILURE() << "Should not have reached this line.";
390 }
391 catch (const ActionError& e)
392 {
393 EXPECT_STREQ(e.what(),
394 "ActionError: i2c_write_bytes: { register: "
395 "0x7C, values: [ 0x56, 0x14, 0xDA ], masks: [ ] }");
396 try
397 {
398 // Re-throw inner I2CException
399 std::rethrow_if_nested(e);
400 ADD_FAILURE() << "Should not have reached this line.";
401 }
402 catch (const i2c::I2CException& ie)
403 {
404 EXPECT_STREQ(ie.what(),
405 "I2CException: Failed to write i2c block data: bus "
406 "/dev/i2c-1, addr 0x70");
407 }
408 catch (...)
409 {
410 ADD_FAILURE() << "Should not have caught exception.";
411 }
412 }
413 catch (...)
414 {
415 ADD_FAILURE() << "Should not have caught exception.";
416 }
417 }
418
TEST(I2CWriteBytesActionTests,GetRegister)419 TEST(I2CWriteBytesActionTests, GetRegister)
420 {
421 std::vector<uint8_t> values{0x56, 0x14};
422 I2CWriteBytesAction action{0xA0, values};
423 EXPECT_EQ(action.getRegister(), 0xA0);
424 }
425
TEST(I2CWriteBytesActionTests,GetValues)426 TEST(I2CWriteBytesActionTests, GetValues)
427 {
428 std::vector<uint8_t> values{0x56, 0x14};
429 std::vector<uint8_t> masks{0x7E, 0x3C};
430 I2CWriteBytesAction action{0xA0, values, masks};
431 EXPECT_EQ(action.getValues().size(), 2);
432 EXPECT_EQ(action.getValues()[0], 0x56);
433 EXPECT_EQ(action.getValues()[1], 0x14);
434 }
435
TEST(I2CWriteBytesActionTests,GetMasks)436 TEST(I2CWriteBytesActionTests, GetMasks)
437 {
438 // Test where masks were not specified
439 {
440 std::vector<uint8_t> values{0x56, 0x14, 0xDA};
441 I2CWriteBytesAction action{0x7C, values};
442 EXPECT_EQ(action.getMasks().size(), 0);
443 }
444
445 // Test where masks were specified
446 {
447 std::vector<uint8_t> values{0x56, 0x14};
448 std::vector<uint8_t> masks{0x7E, 0x3C};
449 I2CWriteBytesAction action{0xA0, values, masks};
450 EXPECT_EQ(action.getMasks().size(), 2);
451 EXPECT_EQ(action.getMasks()[0], 0x7E);
452 EXPECT_EQ(action.getMasks()[1], 0x3C);
453 }
454 }
455
TEST(I2CWriteBytesActionTests,ToString)456 TEST(I2CWriteBytesActionTests, ToString)
457 {
458 // Test where masks were not specified
459 {
460 std::vector<uint8_t> values{0x56, 0x14, 0xDA};
461 I2CWriteBytesAction action{0x7C, values};
462 EXPECT_EQ(action.toString(),
463 "i2c_write_bytes: { register: 0x7C, values: "
464 "[ 0x56, 0x14, 0xDA ], masks: [ ] }");
465 }
466
467 // Test where masks were specified
468 {
469 std::vector<uint8_t> values{0x56, 0x14};
470 std::vector<uint8_t> masks{0x7E, 0x3C};
471 I2CWriteBytesAction action{0xA0, values, masks};
472 EXPECT_EQ(action.toString(),
473 "i2c_write_bytes: { register: 0xA0, values: "
474 "[ 0x56, 0x14 ], masks: [ 0x7E, 0x3C ] }");
475 }
476 }
477