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_compare_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 #include <vector>
31 
32 #include <gmock/gmock.h>
33 #include <gtest/gtest.h>
34 
35 using namespace phosphor::power::regulators;
36 
37 using ::testing::NotNull;
38 using ::testing::Return;
39 using ::testing::SetArrayArgument;
40 using ::testing::Throw;
41 using ::testing::TypedEq;
42 
43 // Test for I2CCompareBytesAction(uint8_t reg,
44 //                                const std::vector<uint8_t>& values)
TEST(I2CCompareBytesActionTests,Constructor1)45 TEST(I2CCompareBytesActionTests, Constructor1)
46 {
47     // Test where works
48     try
49     {
50         std::vector<uint8_t> values{0x56, 0x14, 0xDA};
51         I2CCompareBytesAction action{0x7C, values};
52 
53         EXPECT_EQ(action.getRegister(), 0x7C);
54 
55         EXPECT_EQ(action.getValues().size(), 3);
56         EXPECT_EQ(action.getValues()[0], 0x56);
57         EXPECT_EQ(action.getValues()[1], 0x14);
58         EXPECT_EQ(action.getValues()[2], 0xDA);
59 
60         EXPECT_EQ(action.getMasks().size(), 3);
61         EXPECT_EQ(action.getMasks()[0], 0xFF);
62         EXPECT_EQ(action.getMasks()[1], 0xFF);
63         EXPECT_EQ(action.getMasks()[2], 0xFF);
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         I2CCompareBytesAction 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 I2CCompareBytesAction(uint8_t reg,
88 //                                const std::vector<uint8_t>& values,
89 //                                const std::vector<uint8_t>& masks)
TEST(I2CCompareBytesActionTests,Constructor2)90 TEST(I2CCompareBytesActionTests, 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         I2CCompareBytesAction 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         I2CCompareBytesAction 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         I2CCompareBytesAction 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(I2CCompareBytesActionTests,Execute)149 TEST(I2CCompareBytesActionTests, Execute)
150 {
151     // Test where works: Equal: Mask specified
152     try
153     {
154         // Create mock I2CInterface: read() returns values 0xD7, 0x96
155         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
156             std::make_unique<i2c::MockedI2CInterface>();
157         uint8_t actualValues[] = {0xD7, 0x96};
158         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
159         EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(2), NotNull(),
160                                         i2c::I2CInterface::Mode::I2C))
161             .Times(1)
162             .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 2));
163 
164         // Create Device, IDMap, MockServices, and ActionEnvironment
165         Device device{
166             "reg1", true,
167             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
168             std::move(i2cInterface)};
169         IDMap idMap{};
170         idMap.addDevice(device);
171         MockServices services{};
172         ActionEnvironment env{idMap, "reg1", services};
173 
174         // Actual values: 0xD7 = 1101 0111   0x96 = 1001 0110
175         // Masks        : 0x7E = 0111 1110   0x3C = 0011 1100
176         // Results      : 0x56 = 0101 0110   0x14 = 0001 0100
177         const std::vector<uint8_t> values{0x56, 0x14};
178         const std::vector<uint8_t> masks{0x7E, 0x3C};
179         I2CCompareBytesAction action{0xA0, values, masks};
180         EXPECT_EQ(action.execute(env), true);
181     }
182     catch (...)
183     {
184         ADD_FAILURE() << "Should not have caught exception.";
185     }
186 
187     // Test where works: Equal: Mask not specified
188     try
189     {
190         // Create mock I2CInterface: read() returns values 0x56, 0x14, 0xDA
191         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
192             std::make_unique<i2c::MockedI2CInterface>();
193         uint8_t actualValues[] = {0x56, 0x14, 0xDA};
194         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
195         EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq<uint8_t&>(3), NotNull(),
196                                         i2c::I2CInterface::Mode::I2C))
197             .Times(1)
198             .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 3));
199 
200         // Create Device, IDMap, MockServices, and ActionEnvironment
201         Device device{
202             "reg1", true,
203             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
204             std::move(i2cInterface)};
205         IDMap idMap{};
206         idMap.addDevice(device);
207         MockServices services{};
208         ActionEnvironment env{idMap, "reg1", services};
209 
210         std::vector<uint8_t> values{0x56, 0x14, 0xDA};
211         I2CCompareBytesAction action{0x7C, values};
212         EXPECT_EQ(action.execute(env), true);
213     }
214     catch (...)
215     {
216         ADD_FAILURE() << "Should not have caught exception.";
217     }
218 
219     // Test where works: Not equal: Mask specified
220     try
221     {
222         // Create mock I2CInterface: read() returns values 0xD7, 0x96
223         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
224             std::make_unique<i2c::MockedI2CInterface>();
225         uint8_t actualValues[] = {0xD7, 0x96};
226         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
227         EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(2), NotNull(),
228                                         i2c::I2CInterface::Mode::I2C))
229             .Times(1)
230             .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 2));
231 
232         // Create Device, IDMap, MockServices, and ActionEnvironment
233         Device device{
234             "reg1", true,
235             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
236             std::move(i2cInterface)};
237         IDMap idMap{};
238         idMap.addDevice(device);
239         MockServices services{};
240         ActionEnvironment env{idMap, "reg1", services};
241 
242         // Actual values: 0xD7 = 1101 0111   0x96 = 1001 0110
243         // Masks        : 0x7E = 0111 1110   0x3C = 0011 1100
244         // Results      : 0x56 = 0101 0110   0x14 = 0001 0100
245         const std::vector<uint8_t> values{0x56, 0x13};
246         const std::vector<uint8_t> masks{0x7E, 0x3C};
247         I2CCompareBytesAction action{0xA0, values, masks};
248         EXPECT_EQ(action.execute(env), false);
249     }
250     catch (...)
251     {
252         ADD_FAILURE() << "Should not have caught exception.";
253     }
254 
255     // Test where works: Not equal: Mask not specified
256     try
257     {
258         // Create mock I2CInterface: read() returns values 0x56, 0x14, 0xDA
259         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
260             std::make_unique<i2c::MockedI2CInterface>();
261         uint8_t actualValues[] = {0x56, 0x14, 0xDA};
262         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
263         EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq<uint8_t&>(3), NotNull(),
264                                         i2c::I2CInterface::Mode::I2C))
265             .Times(1)
266             .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 3));
267 
268         // Create Device, IDMap, MockServices, and ActionEnvironment
269         Device device{
270             "reg1", true,
271             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
272             std::move(i2cInterface)};
273         IDMap idMap{};
274         idMap.addDevice(device);
275         MockServices services{};
276         ActionEnvironment env{idMap, "reg1", services};
277 
278         std::vector<uint8_t> values{0x56, 0x14, 0xDB};
279         I2CCompareBytesAction action{0x7C, values};
280         EXPECT_EQ(action.execute(env), false);
281     }
282     catch (...)
283     {
284         ADD_FAILURE() << "Should not have caught exception.";
285     }
286 
287     // Test where works: Single byte
288     try
289     {
290         // Create mock I2CInterface: read() returns value 0xD7
291         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
292             std::make_unique<i2c::MockedI2CInterface>();
293         uint8_t actualValues[] = {0xD7};
294         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
295         EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq<uint8_t&>(1), NotNull(),
296                                         i2c::I2CInterface::Mode::I2C))
297             .Times(1)
298             .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 1));
299 
300         // Create Device, IDMap, MockServices, and ActionEnvironment
301         Device device{
302             "reg1", true,
303             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
304             std::move(i2cInterface)};
305         IDMap idMap{};
306         idMap.addDevice(device);
307         MockServices services{};
308         ActionEnvironment env{idMap, "reg1", services};
309 
310         // Actual values: 0xD7 = 1101 0111
311         // Masks        : 0x7E = 0111 1110
312         // Results      : 0x56 = 0101 0110
313         std::vector<uint8_t> values{0x56};
314         std::vector<uint8_t> masks{0x7E};
315         I2CCompareBytesAction action{0xA0, values, masks};
316         EXPECT_EQ(action.execute(env), true);
317     }
318     catch (...)
319     {
320         ADD_FAILURE() << "Should not have caught exception.";
321     }
322 
323     // Test where fails: Getting I2CInterface fails
324     try
325     {
326         // Create IDMap, MockServices, and ActionEnvironment
327         IDMap idMap{};
328         MockServices services{};
329         ActionEnvironment env{idMap, "reg1", services};
330 
331         std::vector<uint8_t> values{0x56, 0x14, 0xDB};
332         I2CCompareBytesAction action{0x7C, values};
333         action.execute(env);
334         ADD_FAILURE() << "Should not have reached this line.";
335     }
336     catch (const std::invalid_argument& e)
337     {
338         EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
339     }
340     catch (...)
341     {
342         ADD_FAILURE() << "Should not have caught exception.";
343     }
344 
345     // Test where fails: Reading bytes fails
346     try
347     {
348         // Create mock I2CInterface: read() throws an I2CException
349         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
350             std::make_unique<i2c::MockedI2CInterface>();
351         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
352         EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq<uint8_t&>(2), NotNull(),
353                                         i2c::I2CInterface::Mode::I2C))
354             .Times(1)
355             .WillOnce(Throw(i2c::I2CException{"Failed to read i2c block data",
356                                               "/dev/i2c-1", 0x70}));
357 
358         // Create Device, IDMap, MockServices, and ActionEnvironment
359         Device device{
360             "reg1", true,
361             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
362             std::move(i2cInterface)};
363         IDMap idMap{};
364         idMap.addDevice(device);
365         MockServices services{};
366         ActionEnvironment env{idMap, "reg1", services};
367 
368         std::vector<uint8_t> values{0x56, 0x14};
369         I2CCompareBytesAction action{0x7C, values};
370         action.execute(env);
371         ADD_FAILURE() << "Should not have reached this line.";
372     }
373     catch (const ActionError& e)
374     {
375         EXPECT_STREQ(e.what(),
376                      "ActionError: i2c_compare_bytes: { register: "
377                      "0x7C, values: [ 0x56, 0x14 ], masks: [ 0xFF, 0xFF ] }");
378         try
379         {
380             // Re-throw inner I2CException
381             std::rethrow_if_nested(e);
382             ADD_FAILURE() << "Should not have reached this line.";
383         }
384         catch (const i2c::I2CException& ie)
385         {
386             EXPECT_STREQ(ie.what(),
387                          "I2CException: Failed to read i2c block data: bus "
388                          "/dev/i2c-1, addr 0x70");
389         }
390         catch (...)
391         {
392             ADD_FAILURE() << "Should not have caught exception.";
393         }
394     }
395     catch (...)
396     {
397         ADD_FAILURE() << "Should not have caught exception.";
398     }
399 }
400 
TEST(I2CCompareBytesActionTests,GetRegister)401 TEST(I2CCompareBytesActionTests, GetRegister)
402 {
403     std::vector<uint8_t> values{0x56, 0x14};
404     I2CCompareBytesAction action{0xA0, values};
405     EXPECT_EQ(action.getRegister(), 0xA0);
406 }
407 
TEST(I2CCompareBytesActionTests,GetValues)408 TEST(I2CCompareBytesActionTests, GetValues)
409 {
410     std::vector<uint8_t> values{0x56, 0x14};
411     std::vector<uint8_t> masks{0x7E, 0x3C};
412     I2CCompareBytesAction action{0xA0, values, masks};
413     EXPECT_EQ(action.getValues().size(), 2);
414     EXPECT_EQ(action.getValues()[0], 0x56);
415     EXPECT_EQ(action.getValues()[1], 0x14);
416 }
417 
TEST(I2CCompareBytesActionTests,GetMasks)418 TEST(I2CCompareBytesActionTests, GetMasks)
419 {
420     // Test where masks were not specified
421     {
422         std::vector<uint8_t> values{0x56, 0x14, 0xDA};
423         I2CCompareBytesAction action{0x7C, values};
424         EXPECT_EQ(action.getMasks().size(), 3);
425         EXPECT_EQ(action.getMasks()[0], 0xFF);
426         EXPECT_EQ(action.getMasks()[1], 0xFF);
427         EXPECT_EQ(action.getMasks()[2], 0xFF);
428     }
429 
430     // Test where masks were specified
431     {
432         std::vector<uint8_t> values{0x56, 0x14};
433         std::vector<uint8_t> masks{0x7E, 0x3C};
434         I2CCompareBytesAction action{0xA0, values, masks};
435         EXPECT_EQ(action.getMasks().size(), 2);
436         EXPECT_EQ(action.getMasks()[0], 0x7E);
437         EXPECT_EQ(action.getMasks()[1], 0x3C);
438     }
439 }
440 
TEST(I2CCompareBytesActionTests,ToString)441 TEST(I2CCompareBytesActionTests, ToString)
442 {
443     std::vector<uint8_t> values{0x56, 0x14};
444     std::vector<uint8_t> masks{0x7E, 0x3C};
445     I2CCompareBytesAction action{0xA0, values, masks};
446     EXPECT_EQ(action.toString(), "i2c_compare_bytes: { register: 0xA0, values: "
447                                  "[ 0x56, 0x14 ], masks: [ 0x7E, 0x3C ] }");
448 }
449