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