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