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 "device.hpp"
18 #include "i2c_action.hpp"
19 #include "i2c_interface.hpp"
20 #include "id_map.hpp"
21 #include "mock_services.hpp"
22 #include "mocked_i2c_interface.hpp"
23 
24 #include <memory>
25 #include <stdexcept>
26 #include <string>
27 #include <utility>
28 
29 #include <gmock/gmock.h>
30 #include <gtest/gtest.h>
31 
32 using namespace phosphor::power::regulators;
33 
34 using ::testing::Return;
35 using ::testing::Throw;
36 
37 /**
38  * Define a test implementation of the I2CAction abstract base class.
39  */
40 class I2CActionImpl : public I2CAction
41 {
42   public:
43     virtual bool execute(ActionEnvironment& /* environment */) override
44     {
45         return true;
46     }
47 
48     virtual std::string toString() const override
49     {
50         return "i2c_action_impl: {}";
51     }
52 
53     // Make test a friend so it can access protected getI2CInterface() method
54     FRIEND_TEST(I2CActionTests, GetI2CInterface);
55 };
56 
57 TEST(I2CActionTests, GetI2CInterface)
58 {
59     // Test where works: device was not open
60     try
61     {
62         // Create mock I2CInterface
63         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
64             std::make_unique<i2c::MockedI2CInterface>();
65         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(false));
66         EXPECT_CALL(*i2cInterface, open).Times(1);
67 
68         // Create Device, IDMap, MockServices, ActionEnvironment, and I2CAction
69         Device device{
70             "reg1", true,
71             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
72             std::move(i2cInterface)};
73         IDMap idMap{};
74         idMap.addDevice(device);
75         MockServices services{};
76         ActionEnvironment env{idMap, "reg1", services};
77         I2CActionImpl action{};
78 
79         // Get I2CInterface.  Should succeed without an exception.
80         action.getI2CInterface(env);
81     }
82     catch (...)
83     {
84         ADD_FAILURE() << "Should not have caught exception.";
85     }
86 
87     // Test where works: device was already open
88     try
89     {
90         // Create mock I2CInterface
91         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
92             std::make_unique<i2c::MockedI2CInterface>();
93         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
94         EXPECT_CALL(*i2cInterface, open).Times(0);
95 
96         // Create Device, IDMap, MockServices, ActionEnvironment, and I2CAction
97         Device device{
98             "reg1", true,
99             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
100             std::move(i2cInterface)};
101         IDMap idMap{};
102         idMap.addDevice(device);
103         MockServices services{};
104         ActionEnvironment env{idMap, "reg1", services};
105         I2CActionImpl action{};
106 
107         // Get I2CInterface.  Should succeed without an exception.
108         action.getI2CInterface(env);
109     }
110     catch (...)
111     {
112         ADD_FAILURE() << "Should not have caught exception.";
113     }
114 
115     // Test where fails: getting current device fails
116     try
117     {
118         // Create IDMap, MockServices, ActionEnvironment, and I2CAction
119         IDMap idMap{};
120         MockServices services{};
121         ActionEnvironment env{idMap, "reg1", services};
122         I2CActionImpl action{};
123 
124         // Get I2CInterface.  Should throw an exception since "reg1" is not a
125         // valid device in the IDMap.
126         action.getI2CInterface(env);
127         ADD_FAILURE() << "Should not have reached this line.";
128     }
129     catch (const std::invalid_argument& e)
130     {
131         EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
132     }
133     catch (...)
134     {
135         ADD_FAILURE() << "Should not have caught exception.";
136     }
137 
138     // Test where fails: opening interface fails
139     try
140     {
141         // Create mock I2CInterface
142         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
143             std::make_unique<i2c::MockedI2CInterface>();
144         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(false));
145         EXPECT_CALL(*i2cInterface, open)
146             .Times(1)
147             .WillOnce(
148                 Throw(i2c::I2CException{"Failed to open", "/dev/i2c-1", 0x70}));
149 
150         // Create Device, IDMap, ActionEnvironment, and I2CAction
151         Device device{
152             "reg1", true,
153             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
154             std::move(i2cInterface)};
155         IDMap idMap{};
156         idMap.addDevice(device);
157         MockServices services{};
158         ActionEnvironment env{idMap, "reg1", services};
159         I2CActionImpl action{};
160 
161         // Get I2CInterface.  Should throw an exception from the open() call.
162         action.getI2CInterface(env);
163         ADD_FAILURE() << "Should not have reached this line.";
164     }
165     catch (const i2c::I2CException& e)
166     {
167         EXPECT_STREQ(e.what(),
168                      "I2CException: Failed to open: bus /dev/i2c-1, addr 0x70");
169     }
170     catch (...)
171     {
172         ADD_FAILURE() << "Should not have caught exception.";
173     }
174 }
175