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"
17462e5926SBob King #include "chassis.hpp"
18462e5926SBob King #include "compare_presence_action.hpp"
19462e5926SBob King #include "device.hpp"
20462e5926SBob King #include "i2c_interface.hpp"
21bfe2c25aSShawn McCarney #include "mock_action.hpp"
22*81a2f90bSShawn McCarney #include "mock_error_logging.hpp"
23462e5926SBob King #include "mock_journal.hpp"
24462e5926SBob King #include "mock_presence_service.hpp"
25462e5926SBob King #include "mock_services.hpp"
26462e5926SBob King #include "mocked_i2c_interface.hpp"
27bfe2c25aSShawn McCarney #include "presence_detection.hpp"
28462e5926SBob King #include "rule.hpp"
29462e5926SBob King #include "system.hpp"
30bfe2c25aSShawn McCarney 
31*81a2f90bSShawn McCarney #include <sdbusplus/exception.hpp>
32*81a2f90bSShawn McCarney 
33bfe2c25aSShawn McCarney #include <memory>
34462e5926SBob King #include <stdexcept>
35462e5926SBob King #include <string>
36462e5926SBob King #include <tuple>
37bfe2c25aSShawn McCarney #include <utility>
38bfe2c25aSShawn McCarney #include <vector>
39bfe2c25aSShawn McCarney 
40462e5926SBob King #include <gmock/gmock.h>
41bfe2c25aSShawn McCarney #include <gtest/gtest.h>
42bfe2c25aSShawn McCarney 
43bfe2c25aSShawn McCarney using namespace phosphor::power::regulators;
44bfe2c25aSShawn McCarney 
45*81a2f90bSShawn McCarney using ::testing::Ref;
46462e5926SBob King using ::testing::Return;
47462e5926SBob King using ::testing::Throw;
48462e5926SBob King 
49462e5926SBob King /**
50*81a2f90bSShawn McCarney  * Concrete subclass of sdbusplus::exception_t abstract base class.
51*81a2f90bSShawn McCarney  */
52*81a2f90bSShawn McCarney class TestSDBusError : public sdbusplus::exception_t
53*81a2f90bSShawn McCarney {
54*81a2f90bSShawn McCarney   public:
55*81a2f90bSShawn McCarney     TestSDBusError(const std::string& error) : error{error}
56*81a2f90bSShawn McCarney     {
57*81a2f90bSShawn McCarney     }
58*81a2f90bSShawn McCarney 
59*81a2f90bSShawn McCarney     const char* what() const noexcept override
60*81a2f90bSShawn McCarney     {
61*81a2f90bSShawn McCarney         return error.c_str();
62*81a2f90bSShawn McCarney     }
63*81a2f90bSShawn McCarney 
64*81a2f90bSShawn McCarney     const char* name() const noexcept override
65*81a2f90bSShawn McCarney     {
66*81a2f90bSShawn McCarney         return "";
67*81a2f90bSShawn McCarney     }
68*81a2f90bSShawn McCarney 
69*81a2f90bSShawn McCarney     const char* description() const noexcept override
70*81a2f90bSShawn McCarney     {
71*81a2f90bSShawn McCarney         return "";
72*81a2f90bSShawn McCarney     }
73*81a2f90bSShawn McCarney 
74*81a2f90bSShawn McCarney   private:
75*81a2f90bSShawn McCarney     const std::string error{};
76*81a2f90bSShawn McCarney };
77*81a2f90bSShawn McCarney 
78*81a2f90bSShawn McCarney /**
79462e5926SBob King  * Creates the parent objects that normally contain a PresenceDetection object.
80462e5926SBob King  *
81462e5926SBob King  * A PresenceDetection object is normally contained within a hierarchy of
82462e5926SBob King  * System, Chassis, and Device objects.  These objects are required in order to
83462e5926SBob King  * call the execute() method.
84462e5926SBob King  *
85462e5926SBob King  * Creates the System, Chassis, and Device objects.  The PresenceDetection
86462e5926SBob King  * object is moved into the Device object.
87462e5926SBob King  *
88462e5926SBob King  * @param detection PresenceDetection object to move into object hierarchy
89462e5926SBob King  * @return Pointers to the System, Chassis, and Device objects.  The Chassis and
90462e5926SBob King  *         Device objects are contained within the System object and will be
91462e5926SBob King  *         automatically destructed.
92462e5926SBob King  */
93462e5926SBob King std::tuple<std::unique_ptr<System>, Chassis*, Device*>
94462e5926SBob King     createParentObjects(std::unique_ptr<PresenceDetection> detection)
95462e5926SBob King {
96462e5926SBob King     // Create mock I2CInterface
97462e5926SBob King     std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
98462e5926SBob King         std::make_unique<i2c::MockedI2CInterface>();
99462e5926SBob King 
100462e5926SBob King     // Create Device that contains PresenceDetection
101462e5926SBob King     std::unique_ptr<Device> device = std::make_unique<Device>(
102462e5926SBob King         "vdd_reg", true,
103462e5926SBob King         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
104462e5926SBob King         std::move(i2cInterface), std::move(detection));
105462e5926SBob King     Device* devicePtr = device.get();
106462e5926SBob King 
107462e5926SBob King     // Create Chassis that contains Device
108462e5926SBob King     std::vector<std::unique_ptr<Device>> devices{};
109462e5926SBob King     devices.emplace_back(std::move(device));
110462e5926SBob King     std::unique_ptr<Chassis> chassis =
111462e5926SBob King         std::make_unique<Chassis>(1, std::move(devices));
112462e5926SBob King     Chassis* chassisPtr = chassis.get();
113462e5926SBob King 
114462e5926SBob King     // Create System that contains Chassis
115462e5926SBob King     std::vector<std::unique_ptr<Rule>> rules{};
116462e5926SBob King     std::vector<std::unique_ptr<Chassis>> chassisVec{};
117462e5926SBob King     chassisVec.emplace_back(std::move(chassis));
118462e5926SBob King     std::unique_ptr<System> system =
119462e5926SBob King         std::make_unique<System>(std::move(rules), std::move(chassisVec));
120462e5926SBob King 
121462e5926SBob King     return std::make_tuple(std::move(system), chassisPtr, devicePtr);
122462e5926SBob King }
123462e5926SBob King 
124bfe2c25aSShawn McCarney TEST(PresenceDetectionTests, Constructor)
125bfe2c25aSShawn McCarney {
126bfe2c25aSShawn McCarney     std::vector<std::unique_ptr<Action>> actions{};
127462e5926SBob King     actions.emplace_back(std::make_unique<MockAction>());
128bfe2c25aSShawn McCarney 
129462e5926SBob King     PresenceDetection detection{std::move(actions)};
130462e5926SBob King     EXPECT_EQ(detection.getActions().size(), 1);
131462e5926SBob King     EXPECT_FALSE(detection.getCachedPresence().has_value());
132462e5926SBob King }
133462e5926SBob King 
134462e5926SBob King TEST(PresenceDetectionTests, ClearCache)
135462e5926SBob King {
136462e5926SBob King     // Create MockAction that will return true once
137462e5926SBob King     std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
138462e5926SBob King     EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
139462e5926SBob King 
140462e5926SBob King     // Create PresenceDetection
141462e5926SBob King     std::vector<std::unique_ptr<Action>> actions{};
142462e5926SBob King     actions.emplace_back(std::move(action));
143462e5926SBob King     PresenceDetection* detection = new PresenceDetection(std::move(actions));
144462e5926SBob King 
145462e5926SBob King     // Create parent System, Chassis, and Device objects
146462e5926SBob King     auto [system, chassis, device] =
147462e5926SBob King         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
148462e5926SBob King 
149462e5926SBob King     // Verify that initially no presence value is cached
150462e5926SBob King     EXPECT_FALSE(detection->getCachedPresence().has_value());
151462e5926SBob King 
152462e5926SBob King     // Call execute() which should obtain and cache presence value
153462e5926SBob King     MockServices services{};
154462e5926SBob King     EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
155462e5926SBob King 
156462e5926SBob King     // Verify true presence value was cached
157462e5926SBob King     EXPECT_TRUE(detection->getCachedPresence().has_value());
158462e5926SBob King     EXPECT_TRUE(detection->getCachedPresence().value());
159462e5926SBob King 
160462e5926SBob King     // Clear cached presence value
161462e5926SBob King     detection->clearCache();
162462e5926SBob King 
163462e5926SBob King     // Verify that no presence value is cached
164462e5926SBob King     EXPECT_FALSE(detection->getCachedPresence().has_value());
165bfe2c25aSShawn McCarney }
166bfe2c25aSShawn McCarney 
167bfe2c25aSShawn McCarney TEST(PresenceDetectionTests, Execute)
168bfe2c25aSShawn McCarney {
169462e5926SBob King     // Create ComparePresenceAction
170462e5926SBob King     std::unique_ptr<ComparePresenceAction> action =
171462e5926SBob King         std::make_unique<ComparePresenceAction>(
172462e5926SBob King             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2",
173462e5926SBob King             true);
174462e5926SBob King 
175462e5926SBob King     // Create PresenceDetection
176462e5926SBob King     std::vector<std::unique_ptr<Action>> actions{};
177462e5926SBob King     actions.emplace_back(std::move(action));
178462e5926SBob King     PresenceDetection* detection = new PresenceDetection(std::move(actions));
179462e5926SBob King 
180462e5926SBob King     // Create parent System, Chassis, and Device objects
181462e5926SBob King     auto [system, chassis, device] =
182462e5926SBob King         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
183462e5926SBob King 
184462e5926SBob King     // Test where works: Present: Value is not cached
185462e5926SBob King     {
186462e5926SBob King         EXPECT_FALSE(detection->getCachedPresence().has_value());
187462e5926SBob King 
188462e5926SBob King         // Create MockServices.  MockPresenceService::isPresent() should return
189462e5926SBob King         // true.
190462e5926SBob King         MockServices services{};
191462e5926SBob King         MockPresenceService& presenceService =
192462e5926SBob King             services.getMockPresenceService();
193462e5926SBob King         EXPECT_CALL(presenceService,
194462e5926SBob King                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
195462e5926SBob King                               "motherboard/cpu2"))
196462e5926SBob King             .Times(1)
197462e5926SBob King             .WillOnce(Return(true));
198462e5926SBob King 
199462e5926SBob King         // Execute PresenceDetection
200462e5926SBob King         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
201462e5926SBob King 
202462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().has_value());
203462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().value());
204462e5926SBob King     }
205462e5926SBob King 
206462e5926SBob King     // Test where works: Present: Value is cached
207462e5926SBob King     {
208462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().has_value());
209462e5926SBob King 
210462e5926SBob King         // Create MockServices.  MockPresenceService::isPresent() should not be
211462e5926SBob King         // called.
212462e5926SBob King         MockServices services{};
213462e5926SBob King         MockPresenceService& presenceService =
214462e5926SBob King             services.getMockPresenceService();
215462e5926SBob King         EXPECT_CALL(presenceService, isPresent).Times(0);
216462e5926SBob King 
217462e5926SBob King         // Execute PresenceDetection
218462e5926SBob King         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
219462e5926SBob King     }
220462e5926SBob King 
221462e5926SBob King     // Test where works: Not present: Value is not cached
222462e5926SBob King     {
223462e5926SBob King         // Clear cached presence value
224462e5926SBob King         detection->clearCache();
225462e5926SBob King         EXPECT_FALSE(detection->getCachedPresence().has_value());
226462e5926SBob King 
227462e5926SBob King         // Create MockServices.  MockPresenceService::isPresent() should return
228462e5926SBob King         // false.
229462e5926SBob King         MockServices services{};
230462e5926SBob King         MockPresenceService& presenceService =
231462e5926SBob King             services.getMockPresenceService();
232462e5926SBob King         EXPECT_CALL(presenceService,
233462e5926SBob King                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
234462e5926SBob King                               "motherboard/cpu2"))
235462e5926SBob King             .Times(1)
236462e5926SBob King             .WillOnce(Return(false));
237462e5926SBob King 
238462e5926SBob King         // Execute PresenceDetection
239462e5926SBob King         EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
240462e5926SBob King 
241462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().has_value());
242462e5926SBob King         EXPECT_FALSE(detection->getCachedPresence().value());
243462e5926SBob King     }
244462e5926SBob King 
245462e5926SBob King     // Test where works: Not present: Value is cached
246462e5926SBob King     {
247462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().has_value());
248462e5926SBob King 
249462e5926SBob King         // Create MockServices.  MockPresenceService::isPresent() should not be
250462e5926SBob King         // called.
251462e5926SBob King         MockServices services{};
252462e5926SBob King         MockPresenceService& presenceService =
253462e5926SBob King             services.getMockPresenceService();
254462e5926SBob King         EXPECT_CALL(presenceService, isPresent).Times(0);
255462e5926SBob King 
256462e5926SBob King         // Execute PresenceDetection
257462e5926SBob King         EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
258462e5926SBob King     }
259462e5926SBob King 
260462e5926SBob King     // Test where fails
261462e5926SBob King     {
262462e5926SBob King         // Clear cached presence value
263462e5926SBob King         detection->clearCache();
264462e5926SBob King         EXPECT_FALSE(detection->getCachedPresence().has_value());
265462e5926SBob King 
266462e5926SBob King         // Create MockServices.  MockPresenceService::isPresent() should throw
267462e5926SBob King         // an exception.
268462e5926SBob King         MockServices services{};
269462e5926SBob King         MockPresenceService& presenceService =
270462e5926SBob King             services.getMockPresenceService();
271462e5926SBob King         EXPECT_CALL(presenceService,
272462e5926SBob King                     isPresent("/xyz/openbmc_project/inventory/system/chassis/"
273462e5926SBob King                               "motherboard/cpu2"))
274462e5926SBob King             .Times(1)
275*81a2f90bSShawn McCarney             .WillOnce(Throw(TestSDBusError{"DBusError: Invalid object path."}));
276462e5926SBob King 
277462e5926SBob King         // Define expected journal messages that should be passed to MockJournal
278462e5926SBob King         MockJournal& journal = services.getMockJournal();
279462e5926SBob King         std::vector<std::string> exceptionMessages{
280462e5926SBob King             "DBusError: Invalid object path.",
281462e5926SBob King             "ActionError: compare_presence: { fru: "
282462e5926SBob King             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2, "
283462e5926SBob King             "value: true }"};
284462e5926SBob King         EXPECT_CALL(journal, logError(exceptionMessages)).Times(1);
285462e5926SBob King         EXPECT_CALL(journal,
286462e5926SBob King                     logError("Unable to determine presence of vdd_reg"))
287462e5926SBob King             .Times(1);
288462e5926SBob King 
289*81a2f90bSShawn McCarney         // Expect logDBusError() to be called
290*81a2f90bSShawn McCarney         MockErrorLogging& errorLogging = services.getMockErrorLogging();
291*81a2f90bSShawn McCarney         EXPECT_CALL(errorLogging,
292*81a2f90bSShawn McCarney                     logDBusError(Entry::Level::Warning, Ref(journal)))
293*81a2f90bSShawn McCarney             .Times(1);
294*81a2f90bSShawn McCarney 
295462e5926SBob King         // Execute PresenceDetection.  Should return true when an error occurs.
296462e5926SBob King         EXPECT_TRUE(detection->execute(services, *system, *chassis, *device));
297462e5926SBob King 
298462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().has_value());
299462e5926SBob King         EXPECT_TRUE(detection->getCachedPresence().value());
300462e5926SBob King     }
301bfe2c25aSShawn McCarney }
302bfe2c25aSShawn McCarney 
303bfe2c25aSShawn McCarney TEST(PresenceDetectionTests, GetActions)
304bfe2c25aSShawn McCarney {
305bfe2c25aSShawn McCarney     std::vector<std::unique_ptr<Action>> actions{};
306bfe2c25aSShawn McCarney 
307bfe2c25aSShawn McCarney     MockAction* action1 = new MockAction{};
308462e5926SBob King     actions.emplace_back(std::unique_ptr<MockAction>{action1});
309bfe2c25aSShawn McCarney 
310bfe2c25aSShawn McCarney     MockAction* action2 = new MockAction{};
311462e5926SBob King     actions.emplace_back(std::unique_ptr<MockAction>{action2});
312bfe2c25aSShawn McCarney 
313462e5926SBob King     PresenceDetection detection{std::move(actions)};
314462e5926SBob King     EXPECT_EQ(detection.getActions().size(), 2);
315462e5926SBob King     EXPECT_EQ(detection.getActions()[0].get(), action1);
316462e5926SBob King     EXPECT_EQ(detection.getActions()[1].get(), action2);
317462e5926SBob King }
318462e5926SBob King 
319462e5926SBob King TEST(PresenceDetectionTests, GetCachedPresence)
320462e5926SBob King {
321462e5926SBob King     // Create MockAction that will return false once
322462e5926SBob King     std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
323462e5926SBob King     EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(false));
324462e5926SBob King 
325462e5926SBob King     // Create PresenceDetection
326462e5926SBob King     std::vector<std::unique_ptr<Action>> actions{};
327462e5926SBob King     actions.emplace_back(std::move(action));
328462e5926SBob King     PresenceDetection* detection = new PresenceDetection(std::move(actions));
329462e5926SBob King 
330462e5926SBob King     // Create parent System, Chassis, and Device objects
331462e5926SBob King     auto [system, chassis, device] =
332462e5926SBob King         createParentObjects(std::unique_ptr<PresenceDetection>{detection});
333462e5926SBob King 
334462e5926SBob King     // Verify that initially no presence value is cached
335462e5926SBob King     EXPECT_FALSE(detection->getCachedPresence().has_value());
336462e5926SBob King 
337462e5926SBob King     // Call execute() which should obtain and cache presence value
338462e5926SBob King     MockServices services{};
339462e5926SBob King     EXPECT_FALSE(detection->execute(services, *system, *chassis, *device));
340462e5926SBob King 
341462e5926SBob King     // Verify false presence value was cached
342462e5926SBob King     EXPECT_TRUE(detection->getCachedPresence().has_value());
343462e5926SBob King     EXPECT_FALSE(detection->getCachedPresence().value());
344462e5926SBob King 
345462e5926SBob King     // Clear cached presence value
346462e5926SBob King     detection->clearCache();
347462e5926SBob King 
348462e5926SBob King     // Verify that no presence value is cached
349462e5926SBob King     EXPECT_FALSE(detection->getCachedPresence().has_value());
350bfe2c25aSShawn McCarney }
351