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_journal.hpp"
23 #include "mock_services.hpp"
24 #include "mocked_i2c_interface.hpp"
25 #include "pmbus_read_sensor_action.hpp"
26 #include "presence_detection.hpp"
27 #include "rail.hpp"
28 #include "rule.hpp"
29 #include "sensors.hpp"
30 #include "system.hpp"
31 #include "test_utils.hpp"
32 
33 #include <memory>
34 #include <stdexcept>
35 #include <string>
36 #include <utility>
37 #include <vector>
38 
39 #include <gmock/gmock.h>
40 #include <gtest/gtest.h>
41 
42 using namespace phosphor::power::regulators;
43 using namespace phosphor::power::regulators::test_utils;
44 
45 using ::testing::A;
46 using ::testing::Return;
47 using ::testing::TypedEq;
48 
49 // Default chassis inventory path
50 static const std::string defaultInventoryPath{
51     "/xyz/openbmc_project/inventory/system/chassis"};
52 
53 TEST(ChassisTests, Constructor)
54 {
55     // Test where works: Only required parameters are specified
56     {
57         Chassis chassis{2, defaultInventoryPath};
58         EXPECT_EQ(chassis.getNumber(), 2);
59         EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath);
60         EXPECT_EQ(chassis.getDevices().size(), 0);
61     }
62 
63     // Test where works: All parameters are specified
64     {
65         // Create vector of Device objects
66         std::vector<std::unique_ptr<Device>> devices{};
67         devices.emplace_back(createDevice("vdd_reg1"));
68         devices.emplace_back(createDevice("vdd_reg2"));
69 
70         // Create Chassis
71         Chassis chassis{1, defaultInventoryPath, std::move(devices)};
72         EXPECT_EQ(chassis.getNumber(), 1);
73         EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath);
74         EXPECT_EQ(chassis.getDevices().size(), 2);
75     }
76 
77     // Test where fails: Invalid chassis number < 1
78     try
79     {
80         Chassis chassis{0, defaultInventoryPath};
81         ADD_FAILURE() << "Should not have reached this line.";
82     }
83     catch (const std::invalid_argument& e)
84     {
85         EXPECT_STREQ(e.what(), "Invalid chassis number: 0");
86     }
87     catch (...)
88     {
89         ADD_FAILURE() << "Should not have caught exception.";
90     }
91 }
92 
93 TEST(ChassisTests, AddToIDMap)
94 {
95     // Create vector of Device objects
96     std::vector<std::unique_ptr<Device>> devices{};
97     devices.emplace_back(createDevice("reg1", {"rail1"}));
98     devices.emplace_back(createDevice("reg2", {"rail2a", "rail2b"}));
99     devices.emplace_back(createDevice("reg3"));
100 
101     // Create Chassis
102     Chassis chassis{1, defaultInventoryPath, std::move(devices)};
103 
104     // Add Device and Rail objects within the Chassis to an IDMap
105     IDMap idMap{};
106     chassis.addToIDMap(idMap);
107 
108     // Verify all Devices are in the IDMap
109     EXPECT_NO_THROW(idMap.getDevice("reg1"));
110     EXPECT_NO_THROW(idMap.getDevice("reg2"));
111     EXPECT_NO_THROW(idMap.getDevice("reg3"));
112     EXPECT_THROW(idMap.getDevice("reg4"), std::invalid_argument);
113 
114     // Verify all Rails are in the IDMap
115     EXPECT_NO_THROW(idMap.getRail("rail1"));
116     EXPECT_NO_THROW(idMap.getRail("rail2a"));
117     EXPECT_NO_THROW(idMap.getRail("rail2b"));
118     EXPECT_THROW(idMap.getRail("rail3"), std::invalid_argument);
119 }
120 
121 TEST(ChassisTests, ClearCache)
122 {
123     // Create PresenceDetection
124     std::vector<std::unique_ptr<Action>> actions{};
125     std::unique_ptr<PresenceDetection> presenceDetection =
126         std::make_unique<PresenceDetection>(std::move(actions));
127     PresenceDetection* presenceDetectionPtr = presenceDetection.get();
128 
129     // Create Device that contains PresenceDetection
130     std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
131     std::unique_ptr<Device> device = std::make_unique<Device>(
132         "reg1", true,
133         "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
134         std::move(i2cInterface), std::move(presenceDetection));
135     Device* devicePtr = device.get();
136 
137     // Create Chassis that contains Device
138     std::vector<std::unique_ptr<Device>> devices{};
139     devices.emplace_back(std::move(device));
140     std::unique_ptr<Chassis> chassis =
141         std::make_unique<Chassis>(1, defaultInventoryPath, std::move(devices));
142     Chassis* chassisPtr = chassis.get();
143 
144     // Create System that contains Chassis
145     std::vector<std::unique_ptr<Rule>> rules{};
146     std::vector<std::unique_ptr<Chassis>> chassisVec{};
147     chassisVec.emplace_back(std::move(chassis));
148     System system{std::move(rules), std::move(chassisVec)};
149 
150     // Cache presence value in PresenceDetection
151     MockServices services{};
152     presenceDetectionPtr->execute(services, system, *chassisPtr, *devicePtr);
153     EXPECT_TRUE(presenceDetectionPtr->getCachedPresence().has_value());
154 
155     // Clear cached data in Chassis
156     chassisPtr->clearCache();
157 
158     // Verify presence value no longer cached in PresenceDetection
159     EXPECT_FALSE(presenceDetectionPtr->getCachedPresence().has_value());
160 }
161 
162 TEST(ChassisTests, CloseDevices)
163 {
164     // Test where no devices were specified in constructor
165     {
166         // Create mock services.  Expect logDebug() to be called.
167         MockServices services{};
168         MockJournal& journal = services.getMockJournal();
169         EXPECT_CALL(journal, logDebug("Closing devices in chassis 2")).Times(1);
170 
171         // Create Chassis
172         Chassis chassis{2, defaultInventoryPath};
173 
174         // Call closeDevices()
175         chassis.closeDevices(services);
176     }
177 
178     // Test where devices were specified in constructor
179     {
180         std::vector<std::unique_ptr<Device>> devices{};
181 
182         // Create mock services.  Expect logDebug() to be called.
183         MockServices services{};
184         MockJournal& journal = services.getMockJournal();
185         EXPECT_CALL(journal, logDebug("Closing devices in chassis 1")).Times(1);
186 
187         // Create Device vdd0_reg
188         {
189             // Create mock I2CInterface: isOpen() and close() should be called
190             std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
191                 std::make_unique<i2c::MockedI2CInterface>();
192             EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
193             EXPECT_CALL(*i2cInterface, close).Times(1);
194 
195             // Create Device
196             std::unique_ptr<Device> device =
197                 std::make_unique<Device>("vdd0_reg", true,
198                                          "/xyz/openbmc_project/inventory/"
199                                          "system/chassis/motherboard/vdd0_reg",
200                                          std::move(i2cInterface));
201             devices.emplace_back(std::move(device));
202         }
203 
204         // Create Device vdd1_reg
205         {
206             // Create mock I2CInterface: isOpen() and close() should be called
207             std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
208                 std::make_unique<i2c::MockedI2CInterface>();
209             EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
210             EXPECT_CALL(*i2cInterface, close).Times(1);
211 
212             // Create Device
213             std::unique_ptr<Device> device =
214                 std::make_unique<Device>("vdd1_reg", true,
215                                          "/xyz/openbmc_project/inventory/"
216                                          "system/chassis/motherboard/vdd1_reg",
217                                          std::move(i2cInterface));
218             devices.emplace_back(std::move(device));
219         }
220 
221         // Create Chassis
222         Chassis chassis{1, defaultInventoryPath, std::move(devices)};
223 
224         // Call closeDevices()
225         chassis.closeDevices(services);
226     }
227 }
228 
229 TEST(ChassisTests, Configure)
230 {
231     // Test where no devices were specified in constructor
232     {
233         // Create mock services.  Expect logInfo() to be called.
234         MockServices services{};
235         MockJournal& journal = services.getMockJournal();
236         EXPECT_CALL(journal, logInfo("Configuring chassis 1")).Times(1);
237         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
238         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
239 
240         // Create Chassis
241         std::unique_ptr<Chassis> chassis =
242             std::make_unique<Chassis>(1, defaultInventoryPath);
243         Chassis* chassisPtr = chassis.get();
244 
245         // Create System that contains Chassis
246         std::vector<std::unique_ptr<Rule>> rules{};
247         std::vector<std::unique_ptr<Chassis>> chassisVec{};
248         chassisVec.emplace_back(std::move(chassis));
249         System system{std::move(rules), std::move(chassisVec)};
250 
251         // Call configure()
252         chassisPtr->configure(services, system);
253     }
254 
255     // Test where devices were specified in constructor
256     {
257         std::vector<std::unique_ptr<Device>> devices{};
258 
259         // Create mock services.  Expect logInfo() and logDebug() to be called.
260         MockServices services{};
261         MockJournal& journal = services.getMockJournal();
262         EXPECT_CALL(journal, logInfo("Configuring chassis 2")).Times(1);
263         EXPECT_CALL(journal, logDebug("Configuring vdd0_reg: volts=1.300000"))
264             .Times(1);
265         EXPECT_CALL(journal, logDebug("Configuring vdd1_reg: volts=1.200000"))
266             .Times(1);
267         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
268 
269         // Create Device vdd0_reg
270         {
271             // Create Configuration
272             std::vector<std::unique_ptr<Action>> actions{};
273             std::unique_ptr<Configuration> configuration =
274                 std::make_unique<Configuration>(1.3, std::move(actions));
275 
276             // Create Device
277             std::unique_ptr<i2c::I2CInterface> i2cInterface =
278                 createI2CInterface();
279             std::unique_ptr<PresenceDetection> presenceDetection{};
280             std::unique_ptr<Device> device = std::make_unique<Device>(
281                 "vdd0_reg", true,
282                 "/xyz/openbmc_project/inventory/system/chassis/motherboard/"
283                 "vdd0_reg",
284                 std::move(i2cInterface), std::move(presenceDetection),
285                 std::move(configuration));
286             devices.emplace_back(std::move(device));
287         }
288 
289         // Create Device vdd1_reg
290         {
291             // Create Configuration
292             std::vector<std::unique_ptr<Action>> actions{};
293             std::unique_ptr<Configuration> configuration =
294                 std::make_unique<Configuration>(1.2, std::move(actions));
295 
296             // Create Device
297             std::unique_ptr<i2c::I2CInterface> i2cInterface =
298                 createI2CInterface();
299             std::unique_ptr<PresenceDetection> presenceDetection{};
300             std::unique_ptr<Device> device = std::make_unique<Device>(
301                 "vdd1_reg", true,
302                 "/xyz/openbmc_project/inventory/system/chassis/motherboard/"
303                 "vdd1_reg",
304                 std::move(i2cInterface), std::move(presenceDetection),
305                 std::move(configuration));
306             devices.emplace_back(std::move(device));
307         }
308 
309         // Create Chassis
310         std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
311             2, defaultInventoryPath, std::move(devices));
312         Chassis* chassisPtr = chassis.get();
313 
314         // Create System that contains Chassis
315         std::vector<std::unique_ptr<Rule>> rules{};
316         std::vector<std::unique_ptr<Chassis>> chassisVec{};
317         chassisVec.emplace_back(std::move(chassis));
318         System system{std::move(rules), std::move(chassisVec)};
319 
320         // Call configure()
321         chassisPtr->configure(services, system);
322     }
323 }
324 
325 TEST(ChassisTests, GetDevices)
326 {
327     // Test where no devices were specified in constructor
328     {
329         Chassis chassis{2, defaultInventoryPath};
330         EXPECT_EQ(chassis.getDevices().size(), 0);
331     }
332 
333     // Test where devices were specified in constructor
334     {
335         // Create vector of Device objects
336         std::vector<std::unique_ptr<Device>> devices{};
337         devices.emplace_back(createDevice("vdd_reg1"));
338         devices.emplace_back(createDevice("vdd_reg2"));
339 
340         // Create Chassis
341         Chassis chassis{1, defaultInventoryPath, std::move(devices)};
342         EXPECT_EQ(chassis.getDevices().size(), 2);
343         EXPECT_EQ(chassis.getDevices()[0]->getID(), "vdd_reg1");
344         EXPECT_EQ(chassis.getDevices()[1]->getID(), "vdd_reg2");
345     }
346 }
347 
348 TEST(ChassisTests, GetInventoryPath)
349 {
350     Chassis chassis{3, defaultInventoryPath};
351     EXPECT_EQ(chassis.getInventoryPath(), defaultInventoryPath);
352 }
353 
354 TEST(ChassisTests, GetNumber)
355 {
356     Chassis chassis{3, defaultInventoryPath};
357     EXPECT_EQ(chassis.getNumber(), 3);
358 }
359 
360 TEST(ChassisTests, MonitorSensors)
361 {
362     // Test where no devices were specified in constructor
363     {
364         // Create mock services.  No logging should occur.
365         MockServices services{};
366         MockJournal& journal = services.getMockJournal();
367         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
368         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
369 
370         // Create Chassis
371         std::vector<std::unique_ptr<Device>> devices{};
372         std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
373             1, defaultInventoryPath, std::move(devices));
374         Chassis* chassisPtr = chassis.get();
375 
376         // Create System that contains Chassis
377         std::vector<std::unique_ptr<Rule>> rules{};
378         std::vector<std::unique_ptr<Chassis>> chassisVec{};
379         chassisVec.emplace_back(std::move(chassis));
380         System system{std::move(rules), std::move(chassisVec)};
381 
382         // Call monitorSensors().  Should do nothing.
383         chassisPtr->monitorSensors(services, system);
384     }
385 
386     // Test where devices were specified in constructor
387     {
388         // Create mock services.  No logging should occur.
389         MockServices services{};
390         MockJournal& journal = services.getMockJournal();
391         EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
392         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
393 
394         std::vector<std::unique_ptr<Device>> devices{};
395 
396         // Create PMBusReadSensorAction
397         SensorType type{SensorType::iout};
398         uint8_t command = 0x8C;
399         pmbus_utils::SensorDataFormat format{
400             pmbus_utils::SensorDataFormat::linear_11};
401         std::optional<int8_t> exponent{};
402         std::unique_ptr<PMBusReadSensorAction> action =
403             std::make_unique<PMBusReadSensorAction>(type, command, format,
404                                                     exponent);
405 
406         // Create mock I2CInterface.  A two-byte read should occur.
407         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
408             std::make_unique<i2c::MockedI2CInterface>();
409         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
410         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
411             .Times(1);
412 
413         // Create SensorMonitoring
414         std::vector<std::unique_ptr<Action>> actions{};
415         actions.emplace_back(std::move(action));
416         std::unique_ptr<SensorMonitoring> sensorMonitoring =
417             std::make_unique<SensorMonitoring>(std::move(actions));
418 
419         // Create Rail
420         std::vector<std::unique_ptr<Rail>> rails{};
421         std::unique_ptr<Configuration> configuration{};
422         std::unique_ptr<Rail> rail = std::make_unique<Rail>(
423             "vdd0", std::move(configuration), std::move(sensorMonitoring));
424         rails.emplace_back(std::move(rail));
425 
426         // Create Device
427         std::unique_ptr<PresenceDetection> presenceDetection{};
428         std::unique_ptr<Configuration> deviceConfiguration{};
429         std::unique_ptr<Device> device = std::make_unique<Device>(
430             "reg1", true,
431             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
432             std::move(i2cInterface), std::move(presenceDetection),
433             std::move(deviceConfiguration), std::move(rails));
434 
435         // Create Chassis
436         devices.emplace_back(std::move(device));
437         std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
438             1, defaultInventoryPath, std::move(devices));
439         Chassis* chassisPtr = chassis.get();
440 
441         // Create System that contains Chassis
442         std::vector<std::unique_ptr<Rule>> rules{};
443         std::vector<std::unique_ptr<Chassis>> chassisVec{};
444         chassisVec.emplace_back(std::move(chassis));
445         System system{std::move(rules), std::move(chassisVec)};
446 
447         // Call monitorSensors()
448         chassisPtr->monitorSensors(services, system);
449     }
450 }
451