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