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