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