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.hpp"
17 #include "chassis.hpp"
18 #include "compare_presence_action.hpp"
19 #include "device.hpp"
20 #include "i2c_interface.hpp"
21 #include "mock_action.hpp"
22 #include "mock_journal.hpp"
23 #include "mock_presence_service.hpp"
24 #include "mock_services.hpp"
25 #include "mocked_i2c_interface.hpp"
26 #include "presence_detection.hpp"
27 #include "rule.hpp"
28 #include "system.hpp"
29 
30 #include <memory>
31 #include <stdexcept>
32 #include <string>
33 #include <tuple>
34 #include <utility>
35 #include <vector>
36 
37 #include <gmock/gmock.h>
38 #include <gtest/gtest.h>
39 
40 using namespace phosphor::power::regulators;
41 
42 using ::testing::Return;
43 using ::testing::Throw;
44 
45 /**
46  * Creates the parent objects that normally contain a PresenceDetection object.
47  *
48  * A PresenceDetection object is normally contained within a hierarchy of
49  * System, Chassis, and Device objects.  These objects are required in order to
50  * call the execute() method.
51  *
52  * Creates the System, Chassis, and Device objects.  The PresenceDetection
53  * object is moved into the Device object.
54  *
55  * @param detection PresenceDetection object to move into object hierarchy
56  * @return Pointers to the System, Chassis, and Device objects.  The Chassis and
57  *         Device objects are contained within the System object and will be
58  *         automatically destructed.
59  */
60 std::tuple<std::unique_ptr<System>, Chassis*, Device*>
61     createParentObjects(std::unique_ptr<PresenceDetection> detection)
62 {
63     // Create mock I2CInterface
64     std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
65         std::make_unique<i2c::MockedI2CInterface>();
66 
67     // Create Device that contains PresenceDetection
68     std::unique_ptr<Device> device = std::make_unique<Device>(
69         "vdd_reg", true,
70         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
71         std::move(i2cInterface), std::move(detection));
72     Device* devicePtr = device.get();
73 
74     // Create Chassis that contains Device
75     std::vector<std::unique_ptr<Device>> devices{};
76     devices.emplace_back(std::move(device));
77     std::unique_ptr<Chassis> chassis =
78         std::make_unique<Chassis>(1, std::move(devices));
79     Chassis* chassisPtr = chassis.get();
80 
81     // Create System that contains Chassis
82     std::vector<std::unique_ptr<Rule>> rules{};
83     std::vector<std::unique_ptr<Chassis>> chassisVec{};
84     chassisVec.emplace_back(std::move(chassis));
85     std::unique_ptr<System> system =
86         std::make_unique<System>(std::move(rules), std::move(chassisVec));
87 
88     return std::make_tuple(std::move(system), chassisPtr, devicePtr);
89 }
90 
91 TEST(PresenceDetectionTests, Constructor)
92 {
93     std::vector<std::unique_ptr<Action>> actions{};
94     actions.emplace_back(std::make_unique<MockAction>());
95 
96     PresenceDetection detection{std::move(actions)};
97     EXPECT_EQ(detection.getActions().size(), 1);
98     EXPECT_FALSE(detection.getCachedPresence().has_value());
99 }
100 
101 TEST(PresenceDetectionTests, ClearCache)
102 {
103     // Create MockAction that will return true once
104     std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
105     EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
106 
107     // Create PresenceDetection
108     std::vector<std::unique_ptr<Action>> actions{};
109     actions.emplace_back(std::move(action));
110     PresenceDetection* detection = new PresenceDetection(std::move(actions));
111 
112     // Create parent System, Chassis, and Device objects
113     auto [system, chassis, device] =
114         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
115 
116     // Verify that initially no presence value is cached
117     EXPECT_FALSE(detection->getCachedPresence().has_value());
118 
119     // Call execute() which should obtain and cache presence value
120     MockServices services{};
121     EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
122 
123     // Verify true presence value was cached
124     EXPECT_TRUE(detection->getCachedPresence().has_value());
125     EXPECT_TRUE(detection->getCachedPresence().value());
126 
127     // Clear cached presence value
128     detection->clearCache();
129 
130     // Verify that no presence value is cached
131     EXPECT_FALSE(detection->getCachedPresence().has_value());
132 }
133 
134 TEST(PresenceDetectionTests, Execute)
135 {
136     // Create ComparePresenceAction
137     std::unique_ptr<ComparePresenceAction> action =
138         std::make_unique<ComparePresenceAction>(
139             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2",
140             true);
141 
142     // Create PresenceDetection
143     std::vector<std::unique_ptr<Action>> actions{};
144     actions.emplace_back(std::move(action));
145     PresenceDetection* detection = new PresenceDetection(std::move(actions));
146 
147     // Create parent System, Chassis, and Device objects
148     auto [system, chassis, device] =
149         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
150 
151     // Test where works: Present: Value is not cached
152     {
153         EXPECT_FALSE(detection->getCachedPresence().has_value());
154 
155         // Create MockServices.  MockPresenceService::isPresent() should return
156         // true.
157         MockServices services{};
158         MockPresenceService& presenceService =
159             services.getMockPresenceService();
160         EXPECT_CALL(presenceService,
161                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
162                               "motherboard/cpu2"))
163             .Times(1)
164             .WillOnce(Return(true));
165 
166         // Execute PresenceDetection
167         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
168 
169         EXPECT_TRUE(detection->getCachedPresence().has_value());
170         EXPECT_TRUE(detection->getCachedPresence().value());
171     }
172 
173     // Test where works: Present: Value is cached
174     {
175         EXPECT_TRUE(detection->getCachedPresence().has_value());
176 
177         // Create MockServices.  MockPresenceService::isPresent() should not be
178         // called.
179         MockServices services{};
180         MockPresenceService& presenceService =
181             services.getMockPresenceService();
182         EXPECT_CALL(presenceService, isPresent).Times(0);
183 
184         // Execute PresenceDetection
185         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
186     }
187 
188     // Test where works: Not present: Value is not cached
189     {
190         // Clear cached presence value
191         detection->clearCache();
192         EXPECT_FALSE(detection->getCachedPresence().has_value());
193 
194         // Create MockServices.  MockPresenceService::isPresent() should return
195         // false.
196         MockServices services{};
197         MockPresenceService& presenceService =
198             services.getMockPresenceService();
199         EXPECT_CALL(presenceService,
200                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
201                               "motherboard/cpu2"))
202             .Times(1)
203             .WillOnce(Return(false));
204 
205         // Execute PresenceDetection
206         EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
207 
208         EXPECT_TRUE(detection->getCachedPresence().has_value());
209         EXPECT_FALSE(detection->getCachedPresence().value());
210     }
211 
212     // Test where works: Not present: Value is cached
213     {
214         EXPECT_TRUE(detection->getCachedPresence().has_value());
215 
216         // Create MockServices.  MockPresenceService::isPresent() should not be
217         // called.
218         MockServices services{};
219         MockPresenceService& presenceService =
220             services.getMockPresenceService();
221         EXPECT_CALL(presenceService, isPresent).Times(0);
222 
223         // Execute PresenceDetection
224         EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
225     }
226 
227     // Test where fails
228     {
229         // Clear cached presence value
230         detection->clearCache();
231         EXPECT_FALSE(detection->getCachedPresence().has_value());
232 
233         // Create MockServices.  MockPresenceService::isPresent() should throw
234         // an exception.
235         MockServices services{};
236         MockPresenceService& presenceService =
237             services.getMockPresenceService();
238         EXPECT_CALL(presenceService,
239                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
240                               "motherboard/cpu2"))
241             .Times(1)
242             .WillOnce(
243                 Throw(std::runtime_error{"DBusError: Invalid object path."}));
244 
245         // Define expected journal messages that should be passed to MockJournal
246         MockJournal& journal = services.getMockJournal();
247         std::vector<std::string> exceptionMessages{
248             "DBusError: Invalid object path.",
249             "ActionError: compare_presence: { fru: "
250             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2, "
251             "value: true }"};
252         EXPECT_CALL(journal, logError(exceptionMessages)).Times(1);
253         EXPECT_CALL(journal,
254                     logError("Unable to determine presence of vdd_reg"))
255             .Times(1);
256 
257         // Execute PresenceDetection.  Should return true when an error occurs.
258         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
259 
260         EXPECT_TRUE(detection->getCachedPresence().has_value());
261         EXPECT_TRUE(detection->getCachedPresence().value());
262     }
263 }
264 
265 TEST(PresenceDetectionTests, GetActions)
266 {
267     std::vector<std::unique_ptr<Action>> actions{};
268 
269     MockAction* action1 = new MockAction{};
270     actions.emplace_back(std::unique_ptr<MockAction>{action1});
271 
272     MockAction* action2 = new MockAction{};
273     actions.emplace_back(std::unique_ptr<MockAction>{action2});
274 
275     PresenceDetection detection{std::move(actions)};
276     EXPECT_EQ(detection.getActions().size(), 2);
277     EXPECT_EQ(detection.getActions()[0].get(), action1);
278     EXPECT_EQ(detection.getActions()[1].get(), action2);
279 }
280 
281 TEST(PresenceDetectionTests, GetCachedPresence)
282 {
283     // Create MockAction that will return false once
284     std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
285     EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(false));
286 
287     // Create PresenceDetection
288     std::vector<std::unique_ptr<Action>> actions{};
289     actions.emplace_back(std::move(action));
290     PresenceDetection* detection = new PresenceDetection(std::move(actions));
291 
292     // Create parent System, Chassis, and Device objects
293     auto [system, chassis, device] =
294         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
295 
296     // Verify that initially no presence value is cached
297     EXPECT_FALSE(detection->getCachedPresence().has_value());
298 
299     // Call execute() which should obtain and cache presence value
300     MockServices services{};
301     EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
302 
303     // Verify false presence value was cached
304     EXPECT_TRUE(detection->getCachedPresence().has_value());
305     EXPECT_FALSE(detection->getCachedPresence().value());
306 
307     // Clear cached presence value
308     detection->clearCache();
309 
310     // Verify that no presence value is cached
311     EXPECT_FALSE(detection->getCachedPresence().has_value());
312 }
313