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*>
createParentObjects(std::unique_ptr<PresenceDetection> detection)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
TEST(PresenceDetectionTests,Constructor)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
TEST(PresenceDetectionTests,ClearCache)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
TEST(PresenceDetectionTests,Execute)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
TEST(PresenceDetectionTests,GetActions)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
TEST(PresenceDetectionTests,GetCachedPresence)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