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