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