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_services.hpp"
26 #include "mocked_i2c_interface.hpp"
27 #include "pmbus_read_sensor_action.hpp"
28 #include "presence_detection.hpp"
29 #include "rail.hpp"
30 #include "rule.hpp"
31 #include "sensors.hpp"
32 #include "system.hpp"
33 #include "test_utils.hpp"
34 
35 #include <memory>
36 #include <optional>
37 #include <string>
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 using namespace phosphor::power::regulators::test_utils;
46 
47 using ::testing::A;
48 using ::testing::Ref;
49 using ::testing::Return;
50 using ::testing::Throw;
51 using ::testing::TypedEq;
52 
53 static const std::string chassisInvPath{
54     "/xyz/openbmc_project/inventory/system/chassis"};
55 
56 TEST(DeviceTests, Constructor)
57 {
58     // Test where only required parameters are specified
59     {
60         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
61         i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
62         Device device{
63             "vdd_reg", true,
64             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
65             std::move(i2cInterface)};
66         EXPECT_EQ(device.getID(), "vdd_reg");
67         EXPECT_EQ(device.isRegulator(), true);
68         EXPECT_EQ(
69             device.getFRU(),
70             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2");
71         EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
72         EXPECT_EQ(device.getPresenceDetection(), nullptr);
73         EXPECT_EQ(device.getConfiguration(), nullptr);
74         EXPECT_EQ(device.getRails().size(), 0);
75     }
76 
77     // Test where all parameters are specified
78     {
79         // Create I2CInterface
80         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
81         i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
82 
83         // Create PresenceDetection
84         std::vector<std::unique_ptr<Action>> actions{};
85         actions.push_back(std::make_unique<MockAction>());
86         std::unique_ptr<PresenceDetection> presenceDetection =
87             std::make_unique<PresenceDetection>(std::move(actions));
88 
89         // Create Configuration
90         std::optional<double> volts{};
91         actions.clear();
92         actions.push_back(std::make_unique<MockAction>());
93         actions.push_back(std::make_unique<MockAction>());
94         std::unique_ptr<Configuration> configuration =
95             std::make_unique<Configuration>(volts, std::move(actions));
96 
97         // Create vector of Rail objects
98         std::vector<std::unique_ptr<Rail>> rails{};
99         rails.push_back(std::make_unique<Rail>("vdd0"));
100         rails.push_back(std::make_unique<Rail>("vdd1"));
101 
102         // Create Device
103         Device device{
104             "vdd_reg",
105             false,
106             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
107             std::move(i2cInterface),
108             std::move(presenceDetection),
109             std::move(configuration),
110             std::move(rails)};
111         EXPECT_EQ(device.getID(), "vdd_reg");
112         EXPECT_EQ(device.isRegulator(), false);
113         EXPECT_EQ(
114             device.getFRU(),
115             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1");
116         EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
117         EXPECT_NE(device.getPresenceDetection(), nullptr);
118         EXPECT_EQ(device.getPresenceDetection()->getActions().size(), 1);
119         EXPECT_NE(device.getConfiguration(), nullptr);
120         EXPECT_EQ(device.getConfiguration()->getVolts().has_value(), false);
121         EXPECT_EQ(device.getConfiguration()->getActions().size(), 2);
122         EXPECT_EQ(device.getRails().size(), 2);
123     }
124 }
125 
126 TEST(DeviceTests, AddToIDMap)
127 {
128     std::unique_ptr<PresenceDetection> presenceDetection{};
129     std::unique_ptr<Configuration> configuration{};
130 
131     // Create vector of Rail objects
132     std::vector<std::unique_ptr<Rail>> rails{};
133     rails.push_back(std::make_unique<Rail>("vdd0"));
134     rails.push_back(std::make_unique<Rail>("vdd1"));
135 
136     // Create Device
137     Device device{
138         "vdd_reg",
139         false,
140         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
141         std::move(createI2CInterface()),
142         std::move(presenceDetection),
143         std::move(configuration),
144         std::move(rails)};
145 
146     // Add Device and Rail objects to an IDMap
147     IDMap idMap{};
148     device.addToIDMap(idMap);
149 
150     // Verify Device is in the IDMap
151     EXPECT_NO_THROW(idMap.getDevice("vdd_reg"));
152     EXPECT_THROW(idMap.getDevice("vio_reg"), std::invalid_argument);
153 
154     // Verify all Rails are in the IDMap
155     EXPECT_NO_THROW(idMap.getRail("vdd0"));
156     EXPECT_NO_THROW(idMap.getRail("vdd1"));
157     EXPECT_THROW(idMap.getRail("vdd2"), std::invalid_argument);
158 }
159 
160 TEST(DeviceTests, ClearCache)
161 {
162     // Test where Device does not contain a PresenceDetection object
163     try
164     {
165         Device device{
166             "vdd_reg", false,
167             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
168             std::move(createI2CInterface())};
169         device.clearCache();
170     }
171     catch (...)
172     {
173         ADD_FAILURE() << "Should not have caught exception.";
174     }
175 
176     // Test where Device contains a PresenceDetection object
177     {
178         // Create PresenceDetection
179         std::vector<std::unique_ptr<Action>> actions{};
180         std::unique_ptr<PresenceDetection> presenceDetection =
181             std::make_unique<PresenceDetection>(std::move(actions));
182         PresenceDetection* presenceDetectionPtr = presenceDetection.get();
183 
184         // Create Device
185         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
186         std::unique_ptr<Device> device = std::make_unique<Device>(
187             "reg1", true,
188             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
189             std::move(i2cInterface), std::move(presenceDetection));
190         Device* devicePtr = device.get();
191 
192         // Create Chassis that contains Device
193         std::vector<std::unique_ptr<Device>> devices{};
194         devices.emplace_back(std::move(device));
195         std::unique_ptr<Chassis> chassis =
196             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
197         Chassis* chassisPtr = chassis.get();
198 
199         // Create System that contains Chassis
200         std::vector<std::unique_ptr<Rule>> rules{};
201         std::vector<std::unique_ptr<Chassis>> chassisVec{};
202         chassisVec.emplace_back(std::move(chassis));
203         System system{std::move(rules), std::move(chassisVec)};
204 
205         // Cache presence value in PresenceDetection
206         MockServices services{};
207         presenceDetectionPtr->execute(services, system, *chassisPtr,
208                                       *devicePtr);
209         EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
210 
211         // Clear cached data in Device
212         devicePtr->clearCache();
213 
214         // Verify presence value no longer cached in PresenceDetection
215         EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
216     }
217 }
218 
219 TEST(DeviceTests, Close)
220 {
221     // Test where works: I2C interface is not open
222     {
223         // Create mock I2CInterface
224         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
225             std::make_unique<i2c::MockedI2CInterface>();
226         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(false));
227         EXPECT_CALL(*i2cInterface, close).Times(0);
228 
229         // Create mock services.  No logError should occur.
230         MockServices services{};
231         MockJournal& journal = services.getMockJournal();
232         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
233         EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
234             .Times(0);
235 
236         // Create Device
237         Device device{
238             "vdd_reg", true,
239             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
240             std::move(i2cInterface)};
241 
242         // Close Device
243         device.close(services);
244     }
245 
246     // Test where works: I2C interface is open
247     {
248         // Create mock I2CInterface
249         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
250             std::make_unique<i2c::MockedI2CInterface>();
251         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
252         EXPECT_CALL(*i2cInterface, close).Times(1);
253 
254         // Create mock services.  No logError should occur.
255         MockServices services{};
256         MockJournal& journal = services.getMockJournal();
257         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
258         EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
259             .Times(0);
260 
261         // Create Device
262         Device device{
263             "vdd_reg", true,
264             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
265             std::move(i2cInterface)};
266 
267         // Close Device
268         device.close(services);
269     }
270 
271     // Test where fails: closing I2C interface fails
272     {
273         // Create mock I2CInterface
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)
278             .Times(1)
279             .WillOnce(Throw(
280                 i2c::I2CException{"Failed to close", "/dev/i2c-1", 0x70}));
281 
282         // Create mock services.  Expect logError() and logI2CError() to be
283         // called.
284         MockServices services{};
285         MockErrorLogging& errorLogging = services.getMockErrorLogging();
286         MockJournal& journal = services.getMockJournal();
287         std::vector<std::string> expectedErrMessagesException{
288             "I2CException: Failed to close: bus /dev/i2c-1, addr 0x70"};
289         EXPECT_CALL(journal, logError("Unable to close device vdd_reg"))
290             .Times(1);
291         EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1);
292         EXPECT_CALL(errorLogging,
293                     logI2CError(Entry::Level::Notice, Ref(journal),
294                                 "/dev/i2c-1", 0x70, 0))
295             .Times(1);
296 
297         // Create Device
298         Device device{
299             "vdd_reg", true,
300             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
301             std::move(i2cInterface)};
302 
303         // Close Device
304         device.close(services);
305     }
306 }
307 
308 TEST(DeviceTests, Configure)
309 {
310     // Test where device is not present
311     {
312         // Create mock services.  No logging should occur.
313         MockServices services{};
314         MockJournal& journal = services.getMockJournal();
315         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
316         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
317 
318         // Create PresenceDetection.  Indicates device is not present.
319         std::unique_ptr<MockAction> presAction = std::make_unique<MockAction>();
320         EXPECT_CALL(*presAction, execute).Times(1).WillOnce(Return(false));
321         std::vector<std::unique_ptr<Action>> presActions{};
322         presActions.emplace_back(std::move(presAction));
323         std::unique_ptr<PresenceDetection> presenceDetection =
324             std::make_unique<PresenceDetection>(std::move(presActions));
325 
326         // Create Configuration.  Action inside it should not be executed.
327         std::optional<double> volts{};
328         std::unique_ptr<MockAction> confAction = std::make_unique<MockAction>();
329         EXPECT_CALL(*confAction, execute).Times(0);
330         std::vector<std::unique_ptr<Action>> confActions{};
331         confActions.emplace_back(std::move(confAction));
332         std::unique_ptr<Configuration> configuration =
333             std::make_unique<Configuration>(volts, std::move(confActions));
334 
335         // Create Device
336         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
337         std::unique_ptr<Device> device = std::make_unique<Device>(
338             "reg1", true,
339             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
340             std::move(i2cInterface), std::move(presenceDetection),
341             std::move(configuration));
342         Device* devicePtr = device.get();
343 
344         // Create Chassis that contains Device
345         std::vector<std::unique_ptr<Device>> devices{};
346         devices.emplace_back(std::move(device));
347         std::unique_ptr<Chassis> chassis =
348             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
349         Chassis* chassisPtr = chassis.get();
350 
351         // Create System that contains Chassis
352         std::vector<std::unique_ptr<Rule>> rules{};
353         std::vector<std::unique_ptr<Chassis>> chassisVec{};
354         chassisVec.emplace_back(std::move(chassis));
355         System system{std::move(rules), std::move(chassisVec)};
356 
357         // Call configure().  Should do nothing.
358         devicePtr->configure(services, system, *chassisPtr);
359     }
360 
361     // Test where Configuration and Rails were not specified in constructor
362     {
363         // Create mock services.  No logging should occur.
364         MockServices services{};
365         MockJournal& journal = services.getMockJournal();
366         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
367         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
368 
369         // Create Device
370         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
371         std::unique_ptr<Device> device = std::make_unique<Device>(
372             "reg1", true,
373             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
374             std::move(i2cInterface));
375         Device* devicePtr = device.get();
376 
377         // Create Chassis that contains Device
378         std::vector<std::unique_ptr<Device>> devices{};
379         devices.emplace_back(std::move(device));
380         std::unique_ptr<Chassis> chassis =
381             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
382         Chassis* chassisPtr = chassis.get();
383 
384         // Create System that contains Chassis
385         std::vector<std::unique_ptr<Rule>> rules{};
386         std::vector<std::unique_ptr<Chassis>> chassisVec{};
387         chassisVec.emplace_back(std::move(chassis));
388         System system{std::move(rules), std::move(chassisVec)};
389 
390         // Call configure().
391         devicePtr->configure(services, system, *chassisPtr);
392     }
393 
394     // Test where Configuration and Rails were specified in constructor
395     {
396         std::vector<std::unique_ptr<Rail>> rails{};
397 
398         // Create mock services.  Expect logDebug() to be called.
399         // For the Device and both Rails, should execute the Configuration
400         // and log a debug message.
401         MockServices services{};
402         MockJournal& journal = services.getMockJournal();
403         EXPECT_CALL(journal, logDebug("Configuring reg1")).Times(1);
404         EXPECT_CALL(journal, logDebug("Configuring vdd0: volts=1.300000"))
405             .Times(1);
406         EXPECT_CALL(journal, logDebug("Configuring vio0: volts=3.200000"))
407             .Times(1);
408         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
409 
410         // Create Rail vdd0
411         {
412             // Create Configuration for Rail
413             std::optional<double> volts{1.3};
414             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
415             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
416             std::vector<std::unique_ptr<Action>> actions{};
417             actions.emplace_back(std::move(action));
418             std::unique_ptr<Configuration> configuration =
419                 std::make_unique<Configuration>(volts, std::move(actions));
420 
421             // Create Rail
422             std::unique_ptr<Rail> rail =
423                 std::make_unique<Rail>("vdd0", std::move(configuration));
424             rails.emplace_back(std::move(rail));
425         }
426 
427         // Create Rail vio0
428         {
429             // Create Configuration for Rail
430             std::optional<double> volts{3.2};
431             std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
432             EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
433             std::vector<std::unique_ptr<Action>> actions{};
434             actions.emplace_back(std::move(action));
435             std::unique_ptr<Configuration> configuration =
436                 std::make_unique<Configuration>(volts, std::move(actions));
437 
438             // Create Rail
439             std::unique_ptr<Rail> rail =
440                 std::make_unique<Rail>("vio0", std::move(configuration));
441             rails.emplace_back(std::move(rail));
442         }
443 
444         // Create Configuration for Device
445         std::optional<double> volts{};
446         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
447         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
448         std::vector<std::unique_ptr<Action>> actions{};
449         actions.emplace_back(std::move(action));
450         std::unique_ptr<Configuration> configuration =
451             std::make_unique<Configuration>(volts, std::move(actions));
452 
453         // Create Device
454         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
455         std::unique_ptr<PresenceDetection> presenceDetection{};
456         std::unique_ptr<Device> device = std::make_unique<Device>(
457             "reg1", true,
458             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
459             std::move(i2cInterface), std::move(presenceDetection),
460             std::move(configuration), std::move(rails));
461         Device* devicePtr = device.get();
462 
463         // Create Chassis that contains Device
464         std::vector<std::unique_ptr<Device>> devices{};
465         devices.emplace_back(std::move(device));
466         std::unique_ptr<Chassis> chassis =
467             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
468         Chassis* chassisPtr = chassis.get();
469 
470         // Create System that contains Chassis
471         std::vector<std::unique_ptr<Rule>> rules{};
472         std::vector<std::unique_ptr<Chassis>> chassisVec{};
473         chassisVec.emplace_back(std::move(chassis));
474         System system{std::move(rules), std::move(chassisVec)};
475 
476         // Call configure().
477         devicePtr->configure(services, system, *chassisPtr);
478     }
479 }
480 
481 TEST(DeviceTests, GetConfiguration)
482 {
483     // Test where Configuration was not specified in constructor
484     {
485         Device device{
486             "vdd_reg", true,
487             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
488             std::move(createI2CInterface())};
489         EXPECT_EQ(device.getConfiguration(), nullptr);
490     }
491 
492     // Test where Configuration was specified in constructor
493     {
494         std::unique_ptr<PresenceDetection> presenceDetection{};
495 
496         // Create Configuration
497         std::optional<double> volts{3.2};
498         std::vector<std::unique_ptr<Action>> actions{};
499         actions.push_back(std::make_unique<MockAction>());
500         actions.push_back(std::make_unique<MockAction>());
501         std::unique_ptr<Configuration> configuration =
502             std::make_unique<Configuration>(volts, std::move(actions));
503 
504         // Create Device
505         Device device{
506             "vdd_reg",
507             true,
508             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
509             std::move(createI2CInterface()),
510             std::move(presenceDetection),
511             std::move(configuration)};
512         EXPECT_NE(device.getConfiguration(), nullptr);
513         EXPECT_EQ(device.getConfiguration()->getVolts().has_value(), true);
514         EXPECT_EQ(device.getConfiguration()->getVolts().value(), 3.2);
515         EXPECT_EQ(device.getConfiguration()->getActions().size(), 2);
516     }
517 }
518 
519 TEST(DeviceTests, GetFRU)
520 {
521     Device device{
522         "vdd_reg", true,
523         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
524         std::move(createI2CInterface())};
525     EXPECT_EQ(device.getFRU(),
526               "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2");
527 }
528 
529 TEST(DeviceTests, GetI2CInterface)
530 {
531     std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
532     i2c::I2CInterface* i2cInterfacePtr = i2cInterface.get();
533     Device device{
534         "vdd_reg", true,
535         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
536         std::move(i2cInterface)};
537     EXPECT_EQ(&(device.getI2CInterface()), i2cInterfacePtr);
538 }
539 
540 TEST(DeviceTests, GetID)
541 {
542     Device device{
543         "vdd_reg", false,
544         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
545         std::move(createI2CInterface())};
546     EXPECT_EQ(device.getID(), "vdd_reg");
547 }
548 
549 TEST(DeviceTests, GetPresenceDetection)
550 {
551     // Test where PresenceDetection was not specified in constructor
552     {
553         Device device{
554             "vdd_reg", true,
555             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
556             std::move(createI2CInterface())};
557         EXPECT_EQ(device.getPresenceDetection(), nullptr);
558     }
559 
560     // Test where PresenceDetection was specified in constructor
561     {
562         // Create PresenceDetection
563         std::vector<std::unique_ptr<Action>> actions{};
564         actions.push_back(std::make_unique<MockAction>());
565         std::unique_ptr<PresenceDetection> presenceDetection =
566             std::make_unique<PresenceDetection>(std::move(actions));
567 
568         // Create Device
569         Device device{
570             "vdd_reg", false,
571             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
572             std::move(createI2CInterface()), std::move(presenceDetection)};
573         EXPECT_NE(device.getPresenceDetection(), nullptr);
574         EXPECT_EQ(device.getPresenceDetection()->getActions().size(), 1);
575     }
576 }
577 
578 TEST(DeviceTests, GetRails)
579 {
580     // Test where no rails were specified in constructor
581     {
582         Device device{
583             "vdd_reg", true,
584             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
585             std::move(createI2CInterface())};
586         EXPECT_EQ(device.getRails().size(), 0);
587     }
588 
589     // Test where rails were specified in constructor
590     {
591         std::unique_ptr<PresenceDetection> presenceDetection{};
592         std::unique_ptr<Configuration> configuration{};
593 
594         // Create vector of Rail objects
595         std::vector<std::unique_ptr<Rail>> rails{};
596         rails.push_back(std::make_unique<Rail>("vdd0"));
597         rails.push_back(std::make_unique<Rail>("vdd1"));
598 
599         // Create Device
600         Device device{
601             "vdd_reg",
602             false,
603             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
604             std::move(createI2CInterface()),
605             std::move(presenceDetection),
606             std::move(configuration),
607             std::move(rails)};
608         EXPECT_EQ(device.getRails().size(), 2);
609         EXPECT_EQ(device.getRails()[0]->getID(), "vdd0");
610         EXPECT_EQ(device.getRails()[1]->getID(), "vdd1");
611     }
612 }
613 
614 TEST(DeviceTests, IsPresent)
615 {
616     // Test where PresenceDetection not specified in constructor
617     {
618         // Create Device
619         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
620         std::unique_ptr<Device> device = std::make_unique<Device>(
621             "reg1", true,
622             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
623             std::move(i2cInterface));
624         Device* devicePtr = device.get();
625 
626         // Create Chassis that contains Device
627         std::vector<std::unique_ptr<Device>> devices{};
628         devices.emplace_back(std::move(device));
629         std::unique_ptr<Chassis> chassis =
630             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
631         Chassis* chassisPtr = chassis.get();
632 
633         // Create System that contains Chassis
634         std::vector<std::unique_ptr<Rule>> rules{};
635         std::vector<std::unique_ptr<Chassis>> chassisVec{};
636         chassisVec.emplace_back(std::move(chassis));
637         System system{std::move(rules), std::move(chassisVec)};
638 
639         // Create MockServices
640         MockServices services{};
641 
642         // Since no PresenceDetection defined, isPresent() should return true
643         EXPECT_TRUE(devicePtr->isPresent(services, system, *chassisPtr));
644     }
645 
646     // Test where PresenceDetection was specified in constructor: Is present
647     {
648         // Create PresenceDetection.  Indicates device is present.
649         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
650         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
651         std::vector<std::unique_ptr<Action>> actions{};
652         actions.emplace_back(std::move(action));
653         std::unique_ptr<PresenceDetection> presenceDetection =
654             std::make_unique<PresenceDetection>(std::move(actions));
655 
656         // Create Device
657         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
658         std::unique_ptr<Device> device = std::make_unique<Device>(
659             "reg1", true,
660             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
661             std::move(i2cInterface), std::move(presenceDetection));
662         Device* devicePtr = device.get();
663 
664         // Create Chassis that contains Device
665         std::vector<std::unique_ptr<Device>> devices{};
666         devices.emplace_back(std::move(device));
667         std::unique_ptr<Chassis> chassis =
668             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
669         Chassis* chassisPtr = chassis.get();
670 
671         // Create System that contains Chassis
672         std::vector<std::unique_ptr<Rule>> rules{};
673         std::vector<std::unique_ptr<Chassis>> chassisVec{};
674         chassisVec.emplace_back(std::move(chassis));
675         System system{std::move(rules), std::move(chassisVec)};
676 
677         // Create MockServices
678         MockServices services{};
679 
680         // PresenceDetection::execute() and isPresent() should return true
681         EXPECT_TRUE(devicePtr->isPresent(services, system, *chassisPtr));
682     }
683 
684     // Test where PresenceDetection was specified in constructor: Is not present
685     {
686         // Create PresenceDetection.  Indicates device is not present.
687         std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
688         EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(false));
689         std::vector<std::unique_ptr<Action>> actions{};
690         actions.emplace_back(std::move(action));
691         std::unique_ptr<PresenceDetection> presenceDetection =
692             std::make_unique<PresenceDetection>(std::move(actions));
693 
694         // Create Device
695         std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
696         std::unique_ptr<Device> device = std::make_unique<Device>(
697             "reg1", true,
698             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
699             std::move(i2cInterface), std::move(presenceDetection));
700         Device* devicePtr = device.get();
701 
702         // Create Chassis that contains Device
703         std::vector<std::unique_ptr<Device>> devices{};
704         devices.emplace_back(std::move(device));
705         std::unique_ptr<Chassis> chassis =
706             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
707         Chassis* chassisPtr = chassis.get();
708 
709         // Create System that contains Chassis
710         std::vector<std::unique_ptr<Rule>> rules{};
711         std::vector<std::unique_ptr<Chassis>> chassisVec{};
712         chassisVec.emplace_back(std::move(chassis));
713         System system{std::move(rules), std::move(chassisVec)};
714 
715         // Create MockServices
716         MockServices services{};
717 
718         // PresenceDetection::execute() and isPresent() should return false
719         EXPECT_FALSE(devicePtr->isPresent(services, system, *chassisPtr));
720     }
721 }
722 
723 TEST(DeviceTests, IsRegulator)
724 {
725     Device device{
726         "vdd_reg", false,
727         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
728         std::move(createI2CInterface())};
729     EXPECT_EQ(device.isRegulator(), false);
730 }
731 
732 TEST(DeviceTests, MonitorSensors)
733 {
734     // Test where device is not present
735     // TODO: Add this test when sensoring monitoring is fully implemented
736 
737     // Test where Rails were not specified in constructor
738     {
739         // Create mock services.  No logging should occur.
740         MockServices services{};
741         MockJournal& journal = services.getMockJournal();
742         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
743         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
744 
745         // Create mock I2CInterface.  A two-byte read should NOT occur.
746         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
747             std::make_unique<i2c::MockedI2CInterface>();
748         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint16_t&>())).Times(0);
749 
750         // Create Device
751         std::unique_ptr<Device> device = std::make_unique<Device>(
752             "reg1", true,
753             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
754             std::move(i2cInterface));
755         Device* devicePtr = device.get();
756 
757         // Create Chassis that contains Device
758         std::vector<std::unique_ptr<Device>> devices{};
759         devices.emplace_back(std::move(device));
760         std::unique_ptr<Chassis> chassis =
761             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
762         Chassis* chassisPtr = chassis.get();
763 
764         // Create System that contains Chassis
765         std::vector<std::unique_ptr<Rule>> rules{};
766         std::vector<std::unique_ptr<Chassis>> chassisVec{};
767         chassisVec.emplace_back(std::move(chassis));
768         System system{std::move(rules), std::move(chassisVec)};
769 
770         // Call monitorSensors().
771         devicePtr->monitorSensors(services, system, *chassisPtr);
772     }
773 
774     // Test where Rails were specified in constructor
775     {
776         // Create mock services.  No logging should occur.
777         MockServices services{};
778         MockJournal& journal = services.getMockJournal();
779         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
780         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
781 
782         std::vector<std::unique_ptr<Rail>> rails{};
783 
784         // Create PMBusReadSensorAction
785         SensorType type{SensorType::iout};
786         uint8_t command = 0x8C;
787         pmbus_utils::SensorDataFormat format{
788             pmbus_utils::SensorDataFormat::linear_11};
789         std::optional<int8_t> exponent{};
790         std::unique_ptr<PMBusReadSensorAction> action =
791             std::make_unique<PMBusReadSensorAction>(type, command, format,
792                                                     exponent);
793 
794         // Create mock I2CInterface.  A two-byte read should occur.
795         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
796             std::make_unique<i2c::MockedI2CInterface>();
797         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
798         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
799             .Times(1);
800 
801         // Create SensorMonitoring
802         std::vector<std::unique_ptr<Action>> actions{};
803         actions.emplace_back(std::move(action));
804         std::unique_ptr<SensorMonitoring> sensorMonitoring =
805             std::make_unique<SensorMonitoring>(std::move(actions));
806 
807         // Create Rail
808         std::unique_ptr<Configuration> configuration{};
809         std::unique_ptr<Rail> rail = std::make_unique<Rail>(
810             "vdd0", std::move(configuration), std::move(sensorMonitoring));
811         rails.emplace_back(std::move(rail));
812 
813         // Create Device
814         std::unique_ptr<PresenceDetection> presenceDetection{};
815         std::unique_ptr<Configuration> deviceConfiguration{};
816         std::unique_ptr<Device> device = std::make_unique<Device>(
817             "reg1", true,
818             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
819             std::move(i2cInterface), std::move(presenceDetection),
820             std::move(deviceConfiguration), std::move(rails));
821         Device* devicePtr = device.get();
822 
823         // Create Chassis that contains Device
824         std::vector<std::unique_ptr<Device>> devices{};
825         devices.emplace_back(std::move(device));
826         std::unique_ptr<Chassis> chassis =
827             std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
828         Chassis* chassisPtr = chassis.get();
829 
830         // Create System that contains Chassis
831         std::vector<std::unique_ptr<Rule>> rules{};
832         std::vector<std::unique_ptr<Chassis>> chassisVec{};
833         chassisVec.emplace_back(std::move(chassis));
834         System system{std::move(rules), std::move(chassisVec)};
835 
836         // Call monitorSensors().
837         devicePtr->monitorSensors(services, system, *chassisPtr);
838     }
839 }
840