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