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