xref: /openbmc/phosphor-power/phosphor-regulators/test/device_tests.cpp (revision 3225259929b0a97c51ca96b399c8ff149ee99e30)
1 /**
2  * Copyright © 2019 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 "phase_fault_detection.hpp"
29 #include "presence_detection.hpp"
30 #include "rail.hpp"
31 #include "rule.hpp"
32 #include "sensor_monitoring.hpp"
33 #include "sensors.hpp"
34 #include "system.hpp"
35 #include "test_sdbus_error.hpp"
36 #include "test_utils.hpp"
37 
38 #include <memory>
39 #include <optional>
40 #include <string>
41 #include <utility>
42 #include <vector>
43 
44 #include <gmock/gmock.h>
45 #include <gtest/gtest.h>
46 
47 using namespace phosphor::power::regulators;
48 using namespace phosphor::power::regulators::test_utils;
49 
50 using ::testing::A;
51 using ::testing::Ref;
52 using ::testing::Return;
53 using ::testing::Throw;
54 using ::testing::TypedEq;
55 
56 static const std::string chassisInvPath{
57     "/xyz/openbmc_project/inventory/system/chassis"};
58 
59 TEST(DeviceTests, Constructor)
60 {
61     // Test where only required parameters are specified
62     {
63         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
64         i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
65         Device device{
66             "vdd_reg", true,
67             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
68             std::move(i2cInterface)};
69         EXPECT_EQ(device.getID(), "vdd_reg");
70         EXPECT_EQ(device.isRegulator(), true);
71         EXPECT_EQ(
72             device.getFRU(),
73             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2");
74         EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
75         EXPECT_EQ(device.getPresenceDetection(), nullptr);
76         EXPECT_EQ(device.getConfiguration(), nullptr);
77         EXPECT_EQ(device.getPhaseFaultDetection(), nullptr);
78         EXPECT_EQ(device.getRails().size(), 0);
79     }
80 
81     // Test where all parameters are specified
82     {
83         // Create I2CInterface
84         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
85         i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
86 
87         // Create PresenceDetection
88         std::vector<std::unique_ptr<Action>> actions{};
89         actions.push_back(std::make_unique<MockAction>());
90         std::unique_ptr<PresenceDetection> presenceDetection =
91             std::make_unique<PresenceDetection>(std::move(actions));
92 
93         // Create Configuration
94         std::optional<double> volts{};
95         actions.clear();
96         actions.push_back(std::make_unique<MockAction>());
97         actions.push_back(std::make_unique<MockAction>());
98         std::unique_ptr<Configuration> configuration =
99             std::make_unique<Configuration>(volts, std::move(actions));
100 
101         // Create PhaseFaultDetection
102         actions.clear();
103         actions.push_back(std::make_unique<MockAction>());
104         actions.push_back(std::make_unique<MockAction>());
105         actions.push_back(std::make_unique<MockAction>());
106         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
107             std::make_unique<PhaseFaultDetection>(std::move(actions));
108 
109         // Create vector of Rail objects
110         std::vector<std::unique_ptr<Rail>> rails{};
111         rails.push_back(std::make_unique<Rail>("vdd0"));
112         rails.push_back(std::make_unique<Rail>("vdd1"));
113 
114         // Create Device
115         Device device{
116             "vdd_reg",
117             false,
118             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
119             std::move(i2cInterface),
120             std::move(presenceDetection),
121             std::move(configuration),
122             std::move(phaseFaultDetection),
123             std::move(rails)};
124         EXPECT_EQ(device.getID(), "vdd_reg");
125         EXPECT_EQ(device.isRegulator(), false);
126         EXPECT_EQ(
127             device.getFRU(),
128             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1");
129         EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
130         EXPECT_NE(device.getPresenceDetection(), nullptr);
131         EXPECT_EQ(device.getPresenceDetection()->getActions().size(), 1);
132         EXPECT_NE(device.getConfiguration(), nullptr);
133         EXPECT_EQ(device.getConfiguration()->getVolts().has_value(), false);
134         EXPECT_EQ(device.getConfiguration()->getActions().size(), 2);
135         EXPECT_NE(device.getPhaseFaultDetection(), nullptr);
136         EXPECT_EQ(device.getPhaseFaultDetection()->getActions().size(), 3);
137         EXPECT_EQ(device.getRails().size(), 2);
138     }
139 }
140 
141 TEST(DeviceTests, AddToIDMap)
142 {
143     std::unique_ptr<PresenceDetection> presenceDetection{};
144     std::unique_ptr<Configuration> configuration{};
145     std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
146 
147     // Create vector of Rail objects
148     std::vector<std::unique_ptr<Rail>> rails{};
149     rails.push_back(std::make_unique<Rail>("vdd0"));
150     rails.push_back(std::make_unique<Rail>("vdd1"));
151 
152     // Create Device
153     Device device{
154         "vdd_reg",
155         false,
156         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
157         std::move(createI2CInterface()),
158         std::move(presenceDetection),
159         std::move(configuration),
160         std::move(phaseFaultDetection),
161         std::move(rails)};
162 
163     // Add Device and Rail objects to an IDMap
164     IDMap idMap{};
165     device.addToIDMap(idMap);
166 
167     // Verify Device is in the IDMap
168     EXPECT_NO_THROW(idMap.getDevice("vdd_reg"));
169     EXPECT_THROW(idMap.getDevice("vio_reg"), std::invalid_argument);
170 
171     // Verify all Rails are in the IDMap
172     EXPECT_NO_THROW(idMap.getRail("vdd0"));
173     EXPECT_NO_THROW(idMap.getRail("vdd1"));
174     EXPECT_THROW(idMap.getRail("vdd2"), std::invalid_argument);
175 }
176 
177 TEST(DeviceTests, ClearCache)
178 {
179     // Test where Device does not contain a PresenceDetection object
180     try
181     {
182         Device device{
183             "vdd_reg", false,
184             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
185             std::move(createI2CInterface())};
186         device.clearCache();
187     }
188     catch (...)
189     {
190         ADD_FAILURE() << "Should not have caught exception.";
191     }
192 
193     // Test where Device contains a PresenceDetection object
194     {
195         // Create PresenceDetection
196         std::vector<std::unique_ptr<Action>> actions{};
197         std::unique_ptr<PresenceDetection> presenceDetection =
198             std::make_unique<PresenceDetection>(std::move(actions));
199         PresenceDetection* presenceDetectionPtr = presenceDetection.get();
200 
201         // Create Device
202         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
203         std::unique_ptr<Device> device = std::make_unique<Device>(
204             "reg1", true,
205             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
206             std::move(i2cInterface), std::move(presenceDetection));
207         Device* devicePtr = device.get();
208 
209         // Create Chassis that contains Device
210         std::vector<std::unique_ptr<Device>> devices{};
211         devices.emplace_back(std::move(device));
212         std::unique_ptr<Chassis> chassis =
213             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
214         Chassis* chassisPtr = chassis.get();
215 
216         // Create System that contains Chassis
217         std::vector<std::unique_ptr<Rule>> rules{};
218         std::vector<std::unique_ptr<Chassis>> chassisVec{};
219         chassisVec.emplace_back(std::move(chassis));
220         System system{std::move(rules), std::move(chassisVec)};
221 
222         // Cache presence value in PresenceDetection
223         MockServices services{};
224         presenceDetectionPtr->execute(services, system, *chassisPtr,
225                                       *devicePtr);
226         EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
227 
228         // Clear cached data in Device
229         devicePtr->clearCache();
230 
231         // Verify presence value no longer cached in PresenceDetection
232         EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
233     }
234 }
235 
236 TEST(DeviceTests, ClearErrorHistory)
237 {
238     // Create SensorMonitoring.  Will fail with a DBus exception.
239     std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
240     EXPECT_CALL(*action, execute)
241         .WillRepeatedly(Throw(TestSDBusError{"Unable to set sensor value"}));
242     std::vector<std::unique_ptr<Action>> actions{};
243     actions.emplace_back(std::move(action));
244     std::unique_ptr<SensorMonitoring> sensorMonitoring =
245         std::make_unique<SensorMonitoring>(std::move(actions));
246 
247     // Create Rail
248     std::unique_ptr<Configuration> configuration{};
249     std::unique_ptr<Rail> rail = std::make_unique<Rail>(
250         "vddr1", std::move(configuration), std::move(sensorMonitoring));
251 
252     // Create Device that contains Rail
253     std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
254         std::make_unique<i2c::MockedI2CInterface>();
255     std::unique_ptr<PresenceDetection> presenceDetection{};
256     std::unique_ptr<Configuration> deviceConfiguration{};
257     std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
258     std::vector<std::unique_ptr<Rail>> rails{};
259     rails.emplace_back(std::move(rail));
260     std::unique_ptr<Device> device = std::make_unique<Device>(
261         "reg1", true,
262         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
263         std::move(i2cInterface), std::move(presenceDetection),
264         std::move(deviceConfiguration), std::move(phaseFaultDetection),
265         std::move(rails));
266     Device* devicePtr = device.get();
267 
268     // Create Chassis that contains Device
269     std::vector<std::unique_ptr<Device>> devices{};
270     devices.emplace_back(std::move(device));
271     std::unique_ptr<Chassis> chassis =
272         std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
273     Chassis* chassisPtr = chassis.get();
274 
275     // Create System that contains Chassis
276     std::vector<std::unique_ptr<Rule>> rules{};
277     std::vector<std::unique_ptr<Chassis>> chassisVec{};
278     chassisVec.emplace_back(std::move(chassis));
279     System system{std::move(rules), std::move(chassisVec)};
280 
281     // Create mock services
282     MockServices services{};
283 
284     // Expect Sensors service to be called 5+5=10 times
285     MockSensors& sensors = services.getMockSensors();
286     EXPECT_CALL(sensors, startRail).Times(10);
287     EXPECT_CALL(sensors, setValue).Times(0);
288     EXPECT_CALL(sensors, endRail).Times(10);
289 
290     // Expect Journal service to be called 3+3=6 times to log error messages
291     MockJournal& journal = services.getMockJournal();
292     EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
293         .Times(6);
294     EXPECT_CALL(journal, logError(A<const std::string&>())).Times(6);
295 
296     // Expect ErrorLogging service to be called 1+1=2 times to log a DBus error
297     MockErrorLogging& errorLogging = services.getMockErrorLogging();
298     EXPECT_CALL(errorLogging, logDBusError).Times(2);
299 
300     // Monitor sensors 5 times.  Should fail every time, write to journal 3
301     // times, and log one error.
302     for (int i = 1; i <= 5; ++i)
303     {
304         devicePtr->monitorSensors(services, system, *chassisPtr);
305     }
306 
307     // Clear error history
308     devicePtr->clearErrorHistory();
309 
310     // Monitor sensors 5 times again.  Should fail every time, write to journal
311     // 3 times, and log one error.
312     for (int i = 1; i <= 5; ++i)
313     {
314         devicePtr->monitorSensors(services, system, *chassisPtr);
315     }
316 }
317 
318 TEST(DeviceTests, Close)
319 {
320     // Test where works: I2C interface is not open
321     {
322         // Create mock I2CInterface
323         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
324             std::make_unique<i2c::MockedI2CInterface>();
325         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(false));
326         EXPECT_CALL(*i2cInterface, close).Times(0);
327 
328         // Create mock services.  No logError should occur.
329         MockServices services{};
330         MockJournal& journal = services.getMockJournal();
331         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
332         EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
333             .Times(0);
334 
335         // Create Device
336         Device device{
337             "vdd_reg", true,
338             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
339             std::move(i2cInterface)};
340 
341         // Close Device
342         device.close(services);
343     }
344 
345     // Test where works: I2C interface is open
346     {
347         // Create mock I2CInterface
348         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
349             std::make_unique<i2c::MockedI2CInterface>();
350         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
351         EXPECT_CALL(*i2cInterface, close).Times(1);
352 
353         // Create mock services.  No logError should occur.
354         MockServices services{};
355         MockJournal& journal = services.getMockJournal();
356         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
357         EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
358             .Times(0);
359 
360         // Create Device
361         Device device{
362             "vdd_reg", true,
363             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
364             std::move(i2cInterface)};
365 
366         // Close Device
367         device.close(services);
368     }
369 
370     // Test where fails: closing I2C interface fails
371     {
372         // Create mock I2CInterface
373         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
374             std::make_unique<i2c::MockedI2CInterface>();
375         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
376         EXPECT_CALL(*i2cInterface, close)
377             .Times(1)
378             .WillOnce(Throw(
379                 i2c::I2CException{"Failed to close", "/dev/i2c-1", 0x70}));
380 
381         // Create mock services.  Expect logError() and logI2CError() to be
382         // called.
383         MockServices services{};
384         MockErrorLogging& errorLogging = services.getMockErrorLogging();
385         MockJournal& journal = services.getMockJournal();
386         std::vector<std::string> expectedErrMessagesException{
387             "I2CException: Failed to close: bus /dev/i2c-1, addr 0x70"};
388         EXPECT_CALL(journal, logError("Unable to close device vdd_reg"))
389             .Times(1);
390         EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1);
391         EXPECT_CALL(errorLogging,
392                     logI2CError(Entry::Level::Notice, Ref(journal),
393                                 "/dev/i2c-1", 0x70, 0))
394             .Times(1);
395 
396         // Create Device
397         Device device{
398             "vdd_reg", true,
399             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
400             std::move(i2cInterface)};
401 
402         // Close Device
403         device.close(services);
404     }
405 }
406 
407 TEST(DeviceTests, Configure)
408 {
409     // Test where device is not present
410     {
411         // Create mock services.  No logging should occur.
412         MockServices services{};
413         MockJournal& journal = services.getMockJournal();
414         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
415         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
416 
417         // Create PresenceDetection.  Indicates device is not present.
418         std::unique_ptr<MockAction> presAction = std::make_unique<MockAction>();
419         EXPECT_CALL(*presAction, execute).Times(1).WillOnce(Return(false));
420         std::vector<std::unique_ptr<Action>> presActions{};
421         presActions.emplace_back(std::move(presAction));
422         std::unique_ptr<PresenceDetection> presenceDetection =
423             std::make_unique<PresenceDetection>(std::move(presActions));
424 
425         // Create Configuration.  Action inside it should not be executed.
426         std::optional<double> volts{};
427         std::unique_ptr<MockAction> confAction = std::make_unique<MockAction>();
428         EXPECT_CALL(*confAction, execute).Times(0);
429         std::vector<std::unique_ptr<Action>> confActions{};
430         confActions.emplace_back(std::move(confAction));
431         std::unique_ptr<Configuration> configuration =
432             std::make_unique<Configuration>(volts, std::move(confActions));
433 
434         // Create Device
435         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
436         std::unique_ptr<Device> device = std::make_unique<Device>(
437             "reg1", true,
438             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
439             std::move(i2cInterface), std::move(presenceDetection),
440             std::move(configuration));
441         Device* devicePtr = device.get();
442 
443         // Create Chassis that contains Device
444         std::vector<std::unique_ptr<Device>> devices{};
445         devices.emplace_back(std::move(device));
446         std::unique_ptr<Chassis> chassis =
447             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
448         Chassis* chassisPtr = chassis.get();
449 
450         // Create System that contains Chassis
451         std::vector<std::unique_ptr<Rule>> rules{};
452         std::vector<std::unique_ptr<Chassis>> chassisVec{};
453         chassisVec.emplace_back(std::move(chassis));
454         System system{std::move(rules), std::move(chassisVec)};
455 
456         // Call configure().  Should do nothing.
457         devicePtr->configure(services, system, *chassisPtr);
458     }
459 
460     // Test where Configuration and Rails were not specified in constructor
461     {
462         // Create mock services.  No logging should occur.
463         MockServices services{};
464         MockJournal& journal = services.getMockJournal();
465         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
466         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
467 
468         // Create Device
469         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
470         std::unique_ptr<Device> device = std::make_unique<Device>(
471             "reg1", true,
472             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
473             std::move(i2cInterface));
474         Device* devicePtr = device.get();
475 
476         // Create Chassis that contains Device
477         std::vector<std::unique_ptr<Device>> devices{};
478         devices.emplace_back(std::move(device));
479         std::unique_ptr<Chassis> chassis =
480             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
481         Chassis* chassisPtr = chassis.get();
482 
483         // Create System that contains Chassis
484         std::vector<std::unique_ptr<Rule>> rules{};
485         std::vector<std::unique_ptr<Chassis>> chassisVec{};
486         chassisVec.emplace_back(std::move(chassis));
487         System system{std::move(rules), std::move(chassisVec)};
488 
489         // Call configure().
490         devicePtr->configure(services, system, *chassisPtr);
491     }
492 
493     // Test where Configuration and Rails were specified in constructor
494     {
495         std::vector<std::unique_ptr<Rail>> rails{};
496 
497         // Create mock services.  Expect logDebug() to be called.
498         // For the Device and both Rails, should execute the Configuration
499         // and log a debug message.
500         MockServices services{};
501         MockJournal& journal = services.getMockJournal();
502         EXPECT_CALL(journal, logDebug("Configuring reg1")).Times(1);
503         EXPECT_CALL(journal, logDebug("Configuring vdd0: volts=1.300000"))
504             .Times(1);
505         EXPECT_CALL(journal, logDebug("Configuring vio0: volts=3.200000"))
506             .Times(1);
507         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
508 
509         // Create Rail vdd0
510         {
511             // Create Configuration for Rail
512             std::optional<double> volts{1.3};
513             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
514             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
515             std::vector<std::unique_ptr<Action>> actions{};
516             actions.emplace_back(std::move(action));
517             std::unique_ptr<Configuration> configuration =
518                 std::make_unique<Configuration>(volts, std::move(actions));
519 
520             // Create Rail
521             std::unique_ptr<Rail> rail =
522                 std::make_unique<Rail>("vdd0", std::move(configuration));
523             rails.emplace_back(std::move(rail));
524         }
525 
526         // Create Rail vio0
527         {
528             // Create Configuration for Rail
529             std::optional<double> volts{3.2};
530             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
531             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
532             std::vector<std::unique_ptr<Action>> actions{};
533             actions.emplace_back(std::move(action));
534             std::unique_ptr<Configuration> configuration =
535                 std::make_unique<Configuration>(volts, std::move(actions));
536 
537             // Create Rail
538             std::unique_ptr<Rail> rail =
539                 std::make_unique<Rail>("vio0", std::move(configuration));
540             rails.emplace_back(std::move(rail));
541         }
542 
543         // Create Configuration for Device
544         std::optional<double> volts{};
545         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
546         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
547         std::vector<std::unique_ptr<Action>> actions{};
548         actions.emplace_back(std::move(action));
549         std::unique_ptr<Configuration> configuration =
550             std::make_unique<Configuration>(volts, std::move(actions));
551 
552         // Create Device
553         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
554         std::unique_ptr<PresenceDetection> presenceDetection{};
555         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
556         std::unique_ptr<Device> device = std::make_unique<Device>(
557             "reg1", true,
558             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
559             std::move(i2cInterface), std::move(presenceDetection),
560             std::move(configuration), std::move(phaseFaultDetection),
561             std::move(rails));
562         Device* devicePtr = device.get();
563 
564         // Create Chassis that contains Device
565         std::vector<std::unique_ptr<Device>> devices{};
566         devices.emplace_back(std::move(device));
567         std::unique_ptr<Chassis> chassis =
568             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
569         Chassis* chassisPtr = chassis.get();
570 
571         // Create System that contains Chassis
572         std::vector<std::unique_ptr<Rule>> rules{};
573         std::vector<std::unique_ptr<Chassis>> chassisVec{};
574         chassisVec.emplace_back(std::move(chassis));
575         System system{std::move(rules), std::move(chassisVec)};
576 
577         // Call configure().
578         devicePtr->configure(services, system, *chassisPtr);
579     }
580 }
581 
582 TEST(DeviceTests, GetConfiguration)
583 {
584     // Test where Configuration was not specified in constructor
585     {
586         Device device{
587             "vdd_reg", true,
588             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
589             std::move(createI2CInterface())};
590         EXPECT_EQ(device.getConfiguration(), nullptr);
591     }
592 
593     // Test where Configuration was specified in constructor
594     {
595         std::unique_ptr<PresenceDetection> presenceDetection{};
596 
597         // Create Configuration
598         std::optional<double> volts{3.2};
599         std::vector<std::unique_ptr<Action>> actions{};
600         actions.push_back(std::make_unique<MockAction>());
601         actions.push_back(std::make_unique<MockAction>());
602         std::unique_ptr<Configuration> configuration =
603             std::make_unique<Configuration>(volts, std::move(actions));
604 
605         // Create Device
606         Device device{
607             "vdd_reg",
608             true,
609             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
610             std::move(createI2CInterface()),
611             std::move(presenceDetection),
612             std::move(configuration)};
613         EXPECT_NE(device.getConfiguration(), nullptr);
614         EXPECT_EQ(device.getConfiguration()->getVolts().has_value(), true);
615         EXPECT_EQ(device.getConfiguration()->getVolts().value(), 3.2);
616         EXPECT_EQ(device.getConfiguration()->getActions().size(), 2);
617     }
618 }
619 
620 TEST(DeviceTests, GetFRU)
621 {
622     Device device{
623         "vdd_reg", true,
624         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
625         std::move(createI2CInterface())};
626     EXPECT_EQ(device.getFRU(),
627               "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2");
628 }
629 
630 TEST(DeviceTests, GetI2CInterface)
631 {
632     std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
633     i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
634     Device device{
635         "vdd_reg", true,
636         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
637         std::move(i2cInterface)};
638     EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
639 }
640 
641 TEST(DeviceTests, GetID)
642 {
643     Device device{
644         "vdd_reg", false,
645         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
646         std::move(createI2CInterface())};
647     EXPECT_EQ(device.getID(), "vdd_reg");
648 }
649 
650 TEST(DeviceTests, GetPhaseFaultDetection)
651 {
652     // Test where PhaseFaultDetection was not specified in constructor
653     {
654         Device device{
655             "vdd_reg", true,
656             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
657             std::move(createI2CInterface())};
658         EXPECT_EQ(device.getPhaseFaultDetection(), nullptr);
659     }
660 
661     // Test where PhaseFaultDetection was specified in constructor
662     {
663         // Create PhaseFaultDetection
664         std::vector<std::unique_ptr<Action>> actions{};
665         actions.push_back(std::make_unique<MockAction>());
666         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection =
667             std::make_unique<PhaseFaultDetection>(std::move(actions));
668 
669         // Create Device
670         std::unique_ptr<PresenceDetection> presenceDetection{};
671         std::unique_ptr<Configuration> configuration{};
672         Device device{
673             "vdd_reg",
674             false,
675             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
676             std::move(createI2CInterface()),
677             std::move(presenceDetection),
678             std::move(configuration),
679             std::move(phaseFaultDetection)};
680         EXPECT_NE(device.getPhaseFaultDetection(), nullptr);
681         EXPECT_EQ(device.getPhaseFaultDetection()->getActions().size(), 1);
682     }
683 }
684 
685 TEST(DeviceTests, GetPresenceDetection)
686 {
687     // Test where PresenceDetection was not specified in constructor
688     {
689         Device device{
690             "vdd_reg", true,
691             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
692             std::move(createI2CInterface())};
693         EXPECT_EQ(device.getPresenceDetection(), nullptr);
694     }
695 
696     // Test where PresenceDetection was specified in constructor
697     {
698         // Create PresenceDetection
699         std::vector<std::unique_ptr<Action>> actions{};
700         actions.push_back(std::make_unique<MockAction>());
701         std::unique_ptr<PresenceDetection> presenceDetection =
702             std::make_unique<PresenceDetection>(std::move(actions));
703 
704         // Create Device
705         Device device{
706             "vdd_reg", false,
707             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
708             std::move(createI2CInterface()), std::move(presenceDetection)};
709         EXPECT_NE(device.getPresenceDetection(), nullptr);
710         EXPECT_EQ(device.getPresenceDetection()->getActions().size(), 1);
711     }
712 }
713 
714 TEST(DeviceTests, GetRails)
715 {
716     // Test where no rails were specified in constructor
717     {
718         Device device{
719             "vdd_reg", true,
720             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
721             std::move(createI2CInterface())};
722         EXPECT_EQ(device.getRails().size(), 0);
723     }
724 
725     // Test where rails were specified in constructor
726     {
727         std::unique_ptr<PresenceDetection> presenceDetection{};
728         std::unique_ptr<Configuration> configuration{};
729         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
730 
731         // Create vector of Rail objects
732         std::vector<std::unique_ptr<Rail>> rails{};
733         rails.push_back(std::make_unique<Rail>("vdd0"));
734         rails.push_back(std::make_unique<Rail>("vdd1"));
735 
736         // Create Device
737         Device device{
738             "vdd_reg",
739             false,
740             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
741             std::move(createI2CInterface()),
742             std::move(presenceDetection),
743             std::move(configuration),
744             std::move(phaseFaultDetection),
745             std::move(rails)};
746         EXPECT_EQ(device.getRails().size(), 2);
747         EXPECT_EQ(device.getRails()[0]->getID(), "vdd0");
748         EXPECT_EQ(device.getRails()[1]->getID(), "vdd1");
749     }
750 }
751 
752 TEST(DeviceTests, IsPresent)
753 {
754     // Test where PresenceDetection not specified in constructor
755     {
756         // Create Device
757         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
758         std::unique_ptr<Device> device = std::make_unique<Device>(
759             "reg1", true,
760             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
761             std::move(i2cInterface));
762         Device* devicePtr = device.get();
763 
764         // Create Chassis that contains Device
765         std::vector<std::unique_ptr<Device>> devices{};
766         devices.emplace_back(std::move(device));
767         std::unique_ptr<Chassis> chassis =
768             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
769         Chassis* chassisPtr = chassis.get();
770 
771         // Create System that contains Chassis
772         std::vector<std::unique_ptr<Rule>> rules{};
773         std::vector<std::unique_ptr<Chassis>> chassisVec{};
774         chassisVec.emplace_back(std::move(chassis));
775         System system{std::move(rules), std::move(chassisVec)};
776 
777         // Create MockServices
778         MockServices services{};
779 
780         // Since no PresenceDetection defined, isPresent() should return true
781         EXPECT_TRUE(devicePtr->isPresent(services, system, *chassisPtr));
782     }
783 
784     // Test where PresenceDetection was specified in constructor: Is present
785     {
786         // Create PresenceDetection.  Indicates device is present.
787         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
788         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
789         std::vector<std::unique_ptr<Action>> actions{};
790         actions.emplace_back(std::move(action));
791         std::unique_ptr<PresenceDetection> presenceDetection =
792             std::make_unique<PresenceDetection>(std::move(actions));
793 
794         // Create Device
795         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
796         std::unique_ptr<Device> device = std::make_unique<Device>(
797             "reg1", true,
798             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
799             std::move(i2cInterface), std::move(presenceDetection));
800         Device* devicePtr = device.get();
801 
802         // Create Chassis that contains Device
803         std::vector<std::unique_ptr<Device>> devices{};
804         devices.emplace_back(std::move(device));
805         std::unique_ptr<Chassis> chassis =
806             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
807         Chassis* chassisPtr = chassis.get();
808 
809         // Create System that contains Chassis
810         std::vector<std::unique_ptr<Rule>> rules{};
811         std::vector<std::unique_ptr<Chassis>> chassisVec{};
812         chassisVec.emplace_back(std::move(chassis));
813         System system{std::move(rules), std::move(chassisVec)};
814 
815         // Create MockServices
816         MockServices services{};
817 
818         // PresenceDetection::execute() and isPresent() should return true
819         EXPECT_TRUE(devicePtr->isPresent(services, system, *chassisPtr));
820     }
821 
822     // Test where PresenceDetection was specified in constructor: Is not present
823     {
824         // Create PresenceDetection.  Indicates device is not present.
825         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
826         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(false));
827         std::vector<std::unique_ptr<Action>> actions{};
828         actions.emplace_back(std::move(action));
829         std::unique_ptr<PresenceDetection> presenceDetection =
830             std::make_unique<PresenceDetection>(std::move(actions));
831 
832         // Create Device
833         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
834         std::unique_ptr<Device> device = std::make_unique<Device>(
835             "reg1", true,
836             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
837             std::move(i2cInterface), std::move(presenceDetection));
838         Device* devicePtr = device.get();
839 
840         // Create Chassis that contains Device
841         std::vector<std::unique_ptr<Device>> devices{};
842         devices.emplace_back(std::move(device));
843         std::unique_ptr<Chassis> chassis =
844             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
845         Chassis* chassisPtr = chassis.get();
846 
847         // Create System that contains Chassis
848         std::vector<std::unique_ptr<Rule>> rules{};
849         std::vector<std::unique_ptr<Chassis>> chassisVec{};
850         chassisVec.emplace_back(std::move(chassis));
851         System system{std::move(rules), std::move(chassisVec)};
852 
853         // Create MockServices
854         MockServices services{};
855 
856         // PresenceDetection::execute() and isPresent() should return false
857         EXPECT_FALSE(devicePtr->isPresent(services, system, *chassisPtr));
858     }
859 }
860 
861 TEST(DeviceTests, IsRegulator)
862 {
863     Device device{
864         "vdd_reg", false,
865         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
866         std::move(createI2CInterface())};
867     EXPECT_EQ(device.isRegulator(), false);
868 }
869 
870 TEST(DeviceTests, MonitorSensors)
871 {
872     // Test where device is not present
873     {
874         // Create mock services.  No Sensors methods should be called.
875         MockServices services{};
876         MockSensors& sensors = services.getMockSensors();
877         EXPECT_CALL(sensors, startRail).Times(0);
878         EXPECT_CALL(sensors, setValue).Times(0);
879         EXPECT_CALL(sensors, endRail).Times(0);
880 
881         // Create SensorMonitoring.  Action inside it should not be executed.
882         std::unique_ptr<MockAction> sensAction = std::make_unique<MockAction>();
883         EXPECT_CALL(*sensAction, execute).Times(0);
884         std::vector<std::unique_ptr<Action>> sensActions{};
885         sensActions.emplace_back(std::move(sensAction));
886         std::unique_ptr<SensorMonitoring> sensorMonitoring =
887             std::make_unique<SensorMonitoring>(std::move(sensActions));
888 
889         // Create Rail
890         std::unique_ptr<Configuration> configuration{};
891         std::unique_ptr<Rail> rail = std::make_unique<Rail>(
892             "vddr1", std::move(configuration), std::move(sensorMonitoring));
893 
894         // Create PresenceDetection.  Indicates device is not present.
895         std::unique_ptr<MockAction> presAction = std::make_unique<MockAction>();
896         EXPECT_CALL(*presAction, execute).Times(1).WillOnce(Return(false));
897         std::vector<std::unique_ptr<Action>> presActions{};
898         presActions.emplace_back(std::move(presAction));
899         std::unique_ptr<PresenceDetection> presenceDetection =
900             std::make_unique<PresenceDetection>(std::move(presActions));
901 
902         // Create Device
903         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
904         std::unique_ptr<Configuration> deviceConfiguration{};
905         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
906         std::vector<std::unique_ptr<Rail>> rails{};
907         rails.emplace_back(std::move(rail));
908         std::unique_ptr<Device> device = std::make_unique<Device>(
909             "reg1", true,
910             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
911             std::move(i2cInterface), std::move(presenceDetection),
912             std::move(deviceConfiguration), std::move(phaseFaultDetection),
913             std::move(rails));
914         Device* devicePtr = device.get();
915 
916         // Create Chassis that contains Device
917         std::vector<std::unique_ptr<Device>> devices{};
918         devices.emplace_back(std::move(device));
919         std::unique_ptr<Chassis> chassis =
920             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
921         Chassis* chassisPtr = chassis.get();
922 
923         // Create System that contains Chassis
924         std::vector<std::unique_ptr<Rule>> rules{};
925         std::vector<std::unique_ptr<Chassis>> chassisVec{};
926         chassisVec.emplace_back(std::move(chassis));
927         System system{std::move(rules), std::move(chassisVec)};
928 
929         // Call monitorSensors().  Should do nothing.
930         devicePtr->monitorSensors(services, system, *chassisPtr);
931     }
932 
933     // Test where Rails were not specified in constructor
934     {
935         // Create mock services.  No Sensors methods should be called.
936         MockServices services{};
937         MockSensors& sensors = services.getMockSensors();
938         EXPECT_CALL(sensors, startRail).Times(0);
939         EXPECT_CALL(sensors, setValue).Times(0);
940         EXPECT_CALL(sensors, endRail).Times(0);
941 
942         // Create Device
943         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
944         std::unique_ptr<Device> device = std::make_unique<Device>(
945             "reg1", true,
946             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
947             std::move(i2cInterface));
948         Device* devicePtr = device.get();
949 
950         // Create Chassis that contains Device
951         std::vector<std::unique_ptr<Device>> devices{};
952         devices.emplace_back(std::move(device));
953         std::unique_ptr<Chassis> chassis =
954             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
955         Chassis* chassisPtr = chassis.get();
956 
957         // Create System that contains Chassis
958         std::vector<std::unique_ptr<Rule>> rules{};
959         std::vector<std::unique_ptr<Chassis>> chassisVec{};
960         chassisVec.emplace_back(std::move(chassis));
961         System system{std::move(rules), std::move(chassisVec)};
962 
963         // Call monitorSensors().  Should do nothing.
964         devicePtr->monitorSensors(services, system, *chassisPtr);
965     }
966 
967     // Test where Rails were specified in constructor
968     {
969         // Create mock services.  Set Sensors service expectations.
970         MockServices services{};
971         MockSensors& sensors = services.getMockSensors();
972         EXPECT_CALL(sensors, startRail("vdd0",
973                                        "/xyz/openbmc_project/inventory/system/"
974                                        "chassis/motherboard/reg1",
975                                        chassisInvPath))
976             .Times(1);
977         EXPECT_CALL(sensors, startRail("vio0",
978                                        "/xyz/openbmc_project/inventory/system/"
979                                        "chassis/motherboard/reg1",
980                                        chassisInvPath))
981             .Times(1);
982         EXPECT_CALL(sensors, setValue).Times(0);
983         EXPECT_CALL(sensors, endRail(false)).Times(2);
984 
985         std::vector<std::unique_ptr<Rail>> rails{};
986 
987         // Create Rail vdd0
988         {
989             // Create SensorMonitoring for Rail
990             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
991             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
992             std::vector<std::unique_ptr<Action>> actions{};
993             actions.emplace_back(std::move(action));
994             std::unique_ptr<SensorMonitoring> sensorMonitoring =
995                 std::make_unique<SensorMonitoring>(std::move(actions));
996 
997             // Create Rail
998             std::unique_ptr<Configuration> configuration{};
999             std::unique_ptr<Rail> rail = std::make_unique<Rail>(
1000                 "vdd0", std::move(configuration), std::move(sensorMonitoring));
1001             rails.emplace_back(std::move(rail));
1002         }
1003 
1004         // Create Rail vio0
1005         {
1006             // Create SensorMonitoring for Rail
1007             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
1008             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
1009             std::vector<std::unique_ptr<Action>> actions{};
1010             actions.emplace_back(std::move(action));
1011             std::unique_ptr<SensorMonitoring> sensorMonitoring =
1012                 std::make_unique<SensorMonitoring>(std::move(actions));
1013 
1014             // Create Rail
1015             std::unique_ptr<Configuration> configuration{};
1016             std::unique_ptr<Rail> rail = std::make_unique<Rail>(
1017                 "vio0", std::move(configuration), std::move(sensorMonitoring));
1018             rails.emplace_back(std::move(rail));
1019         }
1020 
1021         // Create Device that contains Rails
1022         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
1023         std::unique_ptr<PresenceDetection> presenceDetection{};
1024         std::unique_ptr<Configuration> configuration{};
1025         std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{};
1026         std::unique_ptr<Device> device = std::make_unique<Device>(
1027             "reg1", true,
1028             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
1029             std::move(i2cInterface), std::move(presenceDetection),
1030             std::move(configuration), std::move(phaseFaultDetection),
1031             std::move(rails));
1032         Device* devicePtr = device.get();
1033 
1034         // Create Chassis that contains Device
1035         std::vector<std::unique_ptr<Device>> devices{};
1036         devices.emplace_back(std::move(device));
1037         std::unique_ptr<Chassis> chassis =
1038             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
1039         Chassis* chassisPtr = chassis.get();
1040 
1041         // Create System that contains Chassis
1042         std::vector<std::unique_ptr<Rule>> rules{};
1043         std::vector<std::unique_ptr<Chassis>> chassisVec{};
1044         chassisVec.emplace_back(std::move(chassis));
1045         System system{std::move(rules), std::move(chassisVec)};
1046 
1047         // Call monitorSensors().  Should monitor sensors in both rails.
1048         devicePtr->monitorSensors(services, system, *chassisPtr);
1049     }
1050 }
1051