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 "configuration.hpp"
19 #include "device.hpp"
20 #include "i2c_interface.hpp"
21 #include "id_map.hpp"
22 #include "log_phase_fault_action.hpp"
23 #include "mock_action.hpp"
24 #include "mock_error_logging.hpp"
25 #include "mock_journal.hpp"
26 #include "mock_sensors.hpp"
27 #include "mock_services.hpp"
28 #include "mocked_i2c_interface.hpp"
29 #include "phase_fault.hpp"
30 #include "phase_fault_detection.hpp"
31 #include "presence_detection.hpp"
32 #include "rail.hpp"
33 #include "rule.hpp"
34 #include "sensor_monitoring.hpp"
35 #include "sensors.hpp"
36 #include "services.hpp"
37 #include "system.hpp"
38 #include "test_sdbus_error.hpp"
39 #include "test_utils.hpp"
40
41 #include <memory>
42 #include <stdexcept>
43 #include <string>
44 #include <utility>
45 #include <vector>
46
47 #include <gmock/gmock.h>
48 #include <gtest/gtest.h>
49
50 using namespace phosphor::power::regulators;
51 using namespace phosphor::power::regulators::test_utils;
52
53 using ::testing::A;
54 using ::testing::Return;
55 using ::testing::Throw;
56 using ::testing::TypedEq;
57
58 static const std::string chassisInvPath{
59 "/xyz/openbmc_project/inventory/system/chassis"};
60
TEST(SystemTests,Constructor)61 TEST(SystemTests, Constructor)
62 {
63 // Create Rules
64 std::vector<std::unique_ptr<Rule>> rules{};
65 rules.emplace_back(createRule("set_voltage_rule"));
66
67 // Create Chassis
68 std::vector<std::unique_ptr<Chassis>> chassis{};
69 std::vector<std::unique_ptr<Device>> devices{};
70 devices.emplace_back(createDevice("reg1", {"rail1"}));
71 chassis.emplace_back(
72 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices)));
73
74 // Create System
75 System system{std::move(rules), std::move(chassis)};
76 EXPECT_EQ(system.getChassis().size(), 1);
77 EXPECT_EQ(system.getChassis()[0]->getNumber(), 1);
78 EXPECT_NO_THROW(system.getIDMap().getRule("set_voltage_rule"));
79 EXPECT_NO_THROW(system.getIDMap().getDevice("reg1"));
80 EXPECT_NO_THROW(system.getIDMap().getRail("rail1"));
81 EXPECT_THROW(system.getIDMap().getRail("rail2"), std::invalid_argument);
82 EXPECT_EQ(system.getRules().size(), 1);
83 EXPECT_EQ(system.getRules()[0]->getID(), "set_voltage_rule");
84 }
85
TEST(SystemTests,ClearCache)86 TEST(SystemTests, ClearCache)
87 {
88 // Create PresenceDetection
89 std::vector<std::unique_ptr<Action>> actions{};
90 auto presenceDetection =
91 std::make_unique<PresenceDetection>(std::move(actions));
92 PresenceDetection* presenceDetectionPtr = presenceDetection.get();
93
94 // Create Device that contains PresenceDetection
95 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
96 auto device = std::make_unique<Device>(
97 "reg1", true,
98 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
99 std::move(i2cInterface), std::move(presenceDetection));
100 Device* devicePtr = device.get();
101
102 // Create Chassis that contains Device
103 std::vector<std::unique_ptr<Device>> devices{};
104 devices.emplace_back(std::move(device));
105 auto chassis =
106 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
107 Chassis* chassisPtr = chassis.get();
108
109 // Create System that contains Chassis
110 std::vector<std::unique_ptr<Rule>> rules{};
111 std::vector<std::unique_ptr<Chassis>> chassisVec{};
112 chassisVec.emplace_back(std::move(chassis));
113 System system{std::move(rules), std::move(chassisVec)};
114
115 // Cache presence value in PresenceDetection
116 MockServices services{};
117 presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr);
118 EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
119
120 // Clear cached data in System
121 system.clearCache();
122
123 // Verify presence value no longer cached in PresenceDetection
124 EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
125 }
126
TEST(SystemTests,ClearErrorHistory)127 TEST(SystemTests, ClearErrorHistory)
128 {
129 // Create SensorMonitoring. Will fail with a DBus exception.
130 auto action = std::make_unique<MockAction>();
131 EXPECT_CALL(*action, execute)
132 .WillRepeatedly(Throw(TestSDBusError{"Unable to set sensor value"}));
133 std::vector<std::unique_ptr<Action>> actions{};
134 actions.emplace_back(std::move(action));
135 auto sensorMonitoring =
136 std::make_unique<SensorMonitoring>(std::move(actions));
137
138 // Create Rail
139 std::unique_ptr<Configuration> configuration{};
140 auto rail = std::make_unique<Rail>("vddr1", std::move(configuration),
141 std::move(sensorMonitoring));
142
143 // Create Device that contains Rail
144 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
145 std::unique_ptr<PresenceDetection> presenceDetection{};
146 std::unique_ptr<Configuration> deviceConfiguration{};
147 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
148 std::vector<std::unique_ptr<Rail>> rails{};
149 rails.emplace_back(std::move(rail));
150 auto device = std::make_unique<Device>(
151 "reg1", true,
152 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
153 std::move(i2cInterface), std::move(presenceDetection),
154 std::move(deviceConfiguration), std::move(phaseFaultDetection),
155 std::move(rails));
156
157 // Create Chassis that contains Device
158 std::vector<std::unique_ptr<Device>> devices{};
159 devices.emplace_back(std::move(device));
160 auto chassis =
161 std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
162
163 // Create System that contains Chassis
164 std::vector<std::unique_ptr<Rule>> rules{};
165 std::vector<std::unique_ptr<Chassis>> chassisVec{};
166 chassisVec.emplace_back(std::move(chassis));
167 System system{std::move(rules), std::move(chassisVec)};
168
169 // Create lambda that sets MockServices expectations. The lambda allows
170 // us to set expectations multiple times without duplicate code.
171 auto setExpectations = [](MockServices& services) {
172 // Expect Sensors service to be called 10 times
173 MockSensors& sensors = services.getMockSensors();
174 EXPECT_CALL(sensors, startRail).Times(10);
175 EXPECT_CALL(sensors, setValue).Times(0);
176 EXPECT_CALL(sensors, endRail).Times(10);
177
178 // Expect Journal service to be called 6 times to log error messages
179 MockJournal& journal = services.getMockJournal();
180 EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
181 .Times(6);
182 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(6);
183
184 // Expect ErrorLogging service to be called once to log a DBus error
185 MockErrorLogging& errorLogging = services.getMockErrorLogging();
186 EXPECT_CALL(errorLogging, logDBusError).Times(1);
187 };
188
189 // Monitor sensors 10 times. Verify errors logged.
190 {
191 // Create mock services. Set expectations via lambda.
192 MockServices services{};
193 setExpectations(services);
194
195 for (int i = 1; i <= 10; ++i)
196 {
197 system.monitorSensors(services);
198 }
199 }
200
201 // Clear error history
202 system.clearErrorHistory();
203
204 // Monitor sensors 10 more times. Verify errors logged again.
205 {
206 // Create mock services. Set expectations via lambda.
207 MockServices services{};
208 setExpectations(services);
209
210 for (int i = 1; i <= 10; ++i)
211 {
212 system.monitorSensors(services);
213 }
214 }
215 }
216
TEST(SystemTests,CloseDevices)217 TEST(SystemTests, CloseDevices)
218 {
219 // Specify an empty rules vector
220 std::vector<std::unique_ptr<Rule>> rules{};
221
222 // Create mock services. Expect logDebug() to be called.
223 MockServices services{};
224 MockJournal& journal = services.getMockJournal();
225 EXPECT_CALL(journal, logDebug("Closing devices in chassis 1")).Times(1);
226 EXPECT_CALL(journal, logDebug("Closing devices in chassis 3")).Times(1);
227 EXPECT_CALL(journal, logInfo(A<const std::string&>())).Times(0);
228 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
229
230 // Create Chassis
231 std::vector<std::unique_ptr<Chassis>> chassis{};
232 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1'));
233 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3'));
234
235 // Create System
236 System system{std::move(rules), std::move(chassis)};
237
238 // Call closeDevices()
239 system.closeDevices(services);
240 }
241
TEST(SystemTests,Configure)242 TEST(SystemTests, Configure)
243 {
244 // Create mock services. Expect logInfo() to be called.
245 MockServices services{};
246 MockJournal& journal = services.getMockJournal();
247 EXPECT_CALL(journal, logInfo("Configuring chassis 1")).Times(1);
248 EXPECT_CALL(journal, logInfo("Configuring chassis 3")).Times(1);
249 EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
250 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
251
252 // Specify an empty rules vector
253 std::vector<std::unique_ptr<Rule>> rules{};
254
255 // Create Chassis
256 std::vector<std::unique_ptr<Chassis>> chassis{};
257 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1'));
258 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3'));
259
260 // Create System
261 System system{std::move(rules), std::move(chassis)};
262
263 // Call configure()
264 system.configure(services);
265 }
266
TEST(SystemTests,DetectPhaseFaults)267 TEST(SystemTests, DetectPhaseFaults)
268 {
269 // Create mock services with the following expectations:
270 // - 2 error messages in journal for N phase fault detected in reg0
271 // - 2 error messages in journal for N phase fault detected in reg1
272 // - 1 N phase fault error logged for reg0
273 // - 1 N phase fault error logged for reg1
274 MockServices services{};
275 MockJournal& journal = services.getMockJournal();
276 EXPECT_CALL(journal,
277 logError("n phase fault detected in regulator reg0: count=1"))
278 .Times(1);
279 EXPECT_CALL(journal,
280 logError("n phase fault detected in regulator reg0: count=2"))
281 .Times(1);
282 EXPECT_CALL(journal,
283 logError("n phase fault detected in regulator reg1: count=1"))
284 .Times(1);
285 EXPECT_CALL(journal,
286 logError("n phase fault detected in regulator reg1: count=2"))
287 .Times(1);
288 MockErrorLogging& errorLogging = services.getMockErrorLogging();
289 EXPECT_CALL(errorLogging, logPhaseFault).Times(2);
290
291 std::vector<std::unique_ptr<Chassis>> chassisVec{};
292
293 // Create Chassis 1 with regulator reg0
294 {
295 // Create PhaseFaultDetection
296 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n);
297 std::vector<std::unique_ptr<Action>> actions{};
298 actions.push_back(std::move(action));
299 auto phaseFaultDetection =
300 std::make_unique<PhaseFaultDetection>(std::move(actions));
301
302 // Create Device
303 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
304 std::unique_ptr<PresenceDetection> presenceDetection{};
305 std::unique_ptr<Configuration> configuration{};
306 auto device = std::make_unique<Device>(
307 "reg0", true,
308 "/xyz/openbmc_project/inventory/system/chassis1/motherboard/"
309 "reg0",
310 std::move(i2cInterface), std::move(presenceDetection),
311 std::move(configuration), std::move(phaseFaultDetection));
312
313 // Create Chassis
314 std::vector<std::unique_ptr<Device>> devices{};
315 devices.emplace_back(std::move(device));
316 auto chassis = std::make_unique<Chassis>(1, chassisInvPath + '1',
317 std::move(devices));
318 chassisVec.emplace_back(std::move(chassis));
319 }
320
321 // Create Chassis 2 with regulator reg1
322 {
323 // Create PhaseFaultDetection
324 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n);
325 std::vector<std::unique_ptr<Action>> actions{};
326 actions.push_back(std::move(action));
327 auto phaseFaultDetection =
328 std::make_unique<PhaseFaultDetection>(std::move(actions));
329
330 // Create Device
331 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
332 std::unique_ptr<PresenceDetection> presenceDetection{};
333 std::unique_ptr<Configuration> configuration{};
334 auto device = std::make_unique<Device>(
335 "reg1", true,
336 "/xyz/openbmc_project/inventory/system/chassis2/motherboard/"
337 "reg1",
338 std::move(i2cInterface), std::move(presenceDetection),
339 std::move(configuration), std::move(phaseFaultDetection));
340
341 // Create Chassis
342 std::vector<std::unique_ptr<Device>> devices{};
343 devices.emplace_back(std::move(device));
344 auto chassis = std::make_unique<Chassis>(2, chassisInvPath + '2',
345 std::move(devices));
346 chassisVec.emplace_back(std::move(chassis));
347 }
348
349 // Create System that contains Chassis
350 std::vector<std::unique_ptr<Rule>> rules{};
351 System system{std::move(rules), std::move(chassisVec)};
352
353 // Call detectPhaseFaults() 5 times
354 for (int i = 1; i <= 5; ++i)
355 {
356 system.detectPhaseFaults(services);
357 }
358 }
359
TEST(SystemTests,GetChassis)360 TEST(SystemTests, GetChassis)
361 {
362 // Specify an empty rules vector
363 std::vector<std::unique_ptr<Rule>> rules{};
364
365 // Create Chassis
366 std::vector<std::unique_ptr<Chassis>> chassis{};
367 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1'));
368 chassis.emplace_back(std::make_unique<Chassis>(3, chassisInvPath + '3'));
369
370 // Create System
371 System system{std::move(rules), std::move(chassis)};
372 EXPECT_EQ(system.getChassis().size(), 2);
373 EXPECT_EQ(system.getChassis()[0]->getNumber(), 1);
374 EXPECT_EQ(system.getChassis()[1]->getNumber(), 3);
375 }
376
TEST(SystemTests,GetIDMap)377 TEST(SystemTests, GetIDMap)
378 {
379 // Create Rules
380 std::vector<std::unique_ptr<Rule>> rules{};
381 rules.emplace_back(createRule("set_voltage_rule"));
382 rules.emplace_back(createRule("read_sensors_rule"));
383
384 // Create Chassis
385 std::vector<std::unique_ptr<Chassis>> chassis{};
386 {
387 // Chassis 1
388 std::vector<std::unique_ptr<Device>> devices{};
389 devices.emplace_back(createDevice("reg1", {"rail1"}));
390 devices.emplace_back(createDevice("reg2", {"rail2a", "rail2b"}));
391 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath + '1',
392 std::move(devices)));
393 }
394 {
395 // Chassis 2
396 std::vector<std::unique_ptr<Device>> devices{};
397 devices.emplace_back(createDevice("reg3", {"rail3a", "rail3b"}));
398 devices.emplace_back(createDevice("reg4"));
399 chassis.emplace_back(std::make_unique<Chassis>(2, chassisInvPath + '2',
400 std::move(devices)));
401 }
402
403 // Create System
404 System system{std::move(rules), std::move(chassis)};
405 const IDMap& idMap = system.getIDMap();
406
407 // Verify all Rules are in the IDMap
408 EXPECT_NO_THROW(idMap.getRule("set_voltage_rule"));
409 EXPECT_NO_THROW(idMap.getRule("read_sensors_rule"));
410 EXPECT_THROW(idMap.getRule("set_voltage_rule2"), std::invalid_argument);
411
412 // Verify all Devices are in the IDMap
413 EXPECT_NO_THROW(idMap.getDevice("reg1"));
414 EXPECT_NO_THROW(idMap.getDevice("reg2"));
415 EXPECT_NO_THROW(idMap.getDevice("reg3"));
416 EXPECT_NO_THROW(idMap.getDevice("reg4"));
417 EXPECT_THROW(idMap.getDevice("reg5"), std::invalid_argument);
418
419 // Verify all Rails are in the IDMap
420 EXPECT_NO_THROW(idMap.getRail("rail1"));
421 EXPECT_NO_THROW(idMap.getRail("rail2a"));
422 EXPECT_NO_THROW(idMap.getRail("rail2b"));
423 EXPECT_NO_THROW(idMap.getRail("rail3a"));
424 EXPECT_NO_THROW(idMap.getRail("rail3b"));
425 EXPECT_THROW(idMap.getRail("rail4"), std::invalid_argument);
426 }
427
TEST(SystemTests,GetRules)428 TEST(SystemTests, GetRules)
429 {
430 // Create Rules
431 std::vector<std::unique_ptr<Rule>> rules{};
432 rules.emplace_back(createRule("set_voltage_rule"));
433 rules.emplace_back(createRule("read_sensors_rule"));
434
435 // Create Chassis
436 std::vector<std::unique_ptr<Chassis>> chassis{};
437 chassis.emplace_back(std::make_unique<Chassis>(1, chassisInvPath));
438
439 // Create System
440 System system{std::move(rules), std::move(chassis)};
441 EXPECT_EQ(system.getRules().size(), 2);
442 EXPECT_EQ(system.getRules()[0]->getID(), "set_voltage_rule");
443 EXPECT_EQ(system.getRules()[1]->getID(), "read_sensors_rule");
444 }
445
TEST(SystemTests,MonitorSensors)446 TEST(SystemTests, MonitorSensors)
447 {
448 // Create mock services. Set Sensors service expectations.
449 MockServices services{};
450 MockSensors& sensors = services.getMockSensors();
451 EXPECT_CALL(sensors, startRail("c1_vdd0",
452 "/xyz/openbmc_project/inventory/system/"
453 "chassis1/motherboard/vdd0_reg",
454 chassisInvPath + '1'))
455 .Times(1);
456 EXPECT_CALL(sensors, startRail("c2_vdd0",
457 "/xyz/openbmc_project/inventory/system/"
458 "chassis2/motherboard/vdd0_reg",
459 chassisInvPath + '2'))
460 .Times(1);
461 EXPECT_CALL(sensors, setValue).Times(0);
462 EXPECT_CALL(sensors, endRail(false)).Times(2);
463
464 std::vector<std::unique_ptr<Chassis>> chassisVec{};
465
466 // Create Chassis 1
467 {
468 // Create SensorMonitoring for Rail
469 auto action = std::make_unique<MockAction>();
470 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
471 std::vector<std::unique_ptr<Action>> actions{};
472 actions.emplace_back(std::move(action));
473 auto sensorMonitoring =
474 std::make_unique<SensorMonitoring>(std::move(actions));
475
476 // Create Rail
477 std::unique_ptr<Configuration> configuration{};
478 auto rail = std::make_unique<Rail>("c1_vdd0", std::move(configuration),
479 std::move(sensorMonitoring));
480
481 // Create Device
482 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
483 std::unique_ptr<PresenceDetection> presenceDetection{};
484 std::unique_ptr<Configuration> deviceConfiguration{};
485 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
486 std::vector<std::unique_ptr<Rail>> rails{};
487 rails.emplace_back(std::move(rail));
488 auto device = std::make_unique<Device>(
489 "c1_vdd0_reg", true,
490 "/xyz/openbmc_project/inventory/system/chassis1/motherboard/"
491 "vdd0_reg",
492 std::move(i2cInterface), std::move(presenceDetection),
493 std::move(deviceConfiguration), std::move(phaseFaultDetection),
494 std::move(rails));
495
496 // Create Chassis
497 std::vector<std::unique_ptr<Device>> devices{};
498 devices.emplace_back(std::move(device));
499 auto chassis = std::make_unique<Chassis>(1, chassisInvPath + '1',
500 std::move(devices));
501 chassisVec.emplace_back(std::move(chassis));
502 }
503
504 // Create Chassis 2
505 {
506 // Create SensorMonitoring for Rail
507 auto action = std::make_unique<MockAction>();
508 EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
509 std::vector<std::unique_ptr<Action>> actions{};
510 actions.emplace_back(std::move(action));
511 auto sensorMonitoring =
512 std::make_unique<SensorMonitoring>(std::move(actions));
513
514 // Create Rail
515 std::unique_ptr<Configuration> configuration{};
516 auto rail = std::make_unique<Rail>("c2_vdd0", std::move(configuration),
517 std::move(sensorMonitoring));
518
519 // Create Device
520 auto i2cInterface = std::make_unique<i2c::MockedI2CInterface>();
521 std::unique_ptr<PresenceDetection> presenceDetection{};
522 std::unique_ptr<Configuration> deviceConfiguration{};
523 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
524 std::vector<std::unique_ptr<Rail>> rails{};
525 rails.emplace_back(std::move(rail));
526 auto device = std::make_unique<Device>(
527 "c2_vdd0_reg", true,
528 "/xyz/openbmc_project/inventory/system/chassis2/motherboard/"
529 "vdd0_reg",
530 std::move(i2cInterface), std::move(presenceDetection),
531 std::move(deviceConfiguration), std::move(phaseFaultDetection),
532 std::move(rails));
533
534 // Create Chassis
535 std::vector<std::unique_ptr<Device>> devices{};
536 devices.emplace_back(std::move(device));
537 auto chassis = std::make_unique<Chassis>(2, chassisInvPath + '2',
538 std::move(devices));
539 chassisVec.emplace_back(std::move(chassis));
540 }
541
542 // Create System that contains Chassis
543 std::vector<std::unique_ptr<Rule>> rules{};
544 System system{std::move(rules), std::move(chassisVec)};
545
546 // Call monitorSensors()
547 system.monitorSensors(services);
548 }
549