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 "i2c_write_byte_action.hpp"
22 #include "mock_action.hpp"
23 #include "mock_journal.hpp"
24 #include "mock_services.hpp"
25 #include "mocked_i2c_interface.hpp"
26 #include "pmbus_utils.hpp"
27 #include "pmbus_write_vout_command_action.hpp"
28 #include "presence_detection.hpp"
29 #include "rail.hpp"
30 #include "rule.hpp"
31 #include "system.hpp"
32 
33 #include <cstdint>
34 #include <memory>
35 #include <optional>
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::pmbus_utils;
44 
45 using ::testing::A;
46 using ::testing::Return;
47 using ::testing::Throw;
48 using ::testing::TypedEq;
49 
50 TEST(ConfigurationTests, Constructor)
51 {
52     // Test where volts value specified
53     {
54         std::optional<double> volts{1.3};
55 
56         std::vector<std::unique_ptr<Action>> actions{};
57         actions.push_back(std::make_unique<MockAction>());
58         actions.push_back(std::make_unique<MockAction>());
59 
60         Configuration configuration(volts, std::move(actions));
61         EXPECT_EQ(configuration.getVolts().has_value(), true);
62         EXPECT_EQ(configuration.getVolts().value(), 1.3);
63         EXPECT_EQ(configuration.getActions().size(), 2);
64     }
65 
66     // Test where volts value not specified
67     {
68         std::optional<double> volts{};
69 
70         std::vector<std::unique_ptr<Action>> actions{};
71         actions.push_back(std::make_unique<MockAction>());
72 
73         Configuration configuration(volts, std::move(actions));
74         EXPECT_EQ(configuration.getVolts().has_value(), false);
75         EXPECT_EQ(configuration.getActions().size(), 1);
76     }
77 }
78 
79 // Test for execute(Services&, System&, Chassis&, Device&)
80 TEST(ConfigurationTests, ExecuteForDevice)
81 {
82     // Test where works: Volts value not specified
83     {
84         // Create mock services.  Expect logDebug() to be called.
85         MockServices services{};
86         MockJournal& journal = services.getMockJournal();
87         EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1);
88         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
89 
90         // Create I2CWriteByteAction with register 0x7C and value 0x0A
91         std::unique_ptr<I2CWriteByteAction> action =
92             std::make_unique<I2CWriteByteAction>(0x7C, 0x0A);
93 
94         // Create mock I2CInterface.  Expect action to write 0x0A to 0x7C.
95         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
96             std::make_unique<i2c::MockedI2CInterface>();
97         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
98         EXPECT_CALL(*i2cInterface,
99                     write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A)))
100             .Times(1);
101 
102         // Create Configuration with no volts value specified
103         std::optional<double> volts{};
104         std::vector<std::unique_ptr<Action>> actions{};
105         actions.emplace_back(std::move(action));
106         std::unique_ptr<Configuration> configuration =
107             std::make_unique<Configuration>(volts, std::move(actions));
108         Configuration* configurationPtr = configuration.get();
109 
110         // Create Device that contains Configuration
111         std::unique_ptr<PresenceDetection> presenceDetection{};
112         std::unique_ptr<Device> device = std::make_unique<Device>(
113             "vdd_reg", true,
114             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
115             std::move(i2cInterface), std::move(presenceDetection),
116             std::move(configuration));
117         Device* devicePtr = device.get();
118 
119         // Create Chassis that contains Device
120         std::vector<std::unique_ptr<Device>> devices{};
121         devices.emplace_back(std::move(device));
122         std::unique_ptr<Chassis> chassis =
123             std::make_unique<Chassis>(1, std::move(devices));
124         Chassis* chassisPtr = chassis.get();
125 
126         // Create System that contains Chassis
127         std::vector<std::unique_ptr<Rule>> rules{};
128         std::vector<std::unique_ptr<Chassis>> chassisVec{};
129         chassisVec.emplace_back(std::move(chassis));
130         System system{std::move(rules), std::move(chassisVec)};
131 
132         // Execute Configuration
133         configurationPtr->execute(services, system, *chassisPtr, *devicePtr);
134     }
135 
136     // Test where works: Volts value specified
137     {
138         // Create mock services.  Expect logDebug() to be called.
139         MockServices services{};
140         MockJournal& journal = services.getMockJournal();
141         EXPECT_CALL(journal, logDebug("Configuring vdd_reg: volts=1.300000"))
142             .Times(1);
143         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
144 
145         // Create PMBusWriteVoutCommandAction.  Do not specify a volts value
146         // because it will get a value of 1.3V from the
147         // ActionEnvironment/Configuration.  Specify a -8 exponent.
148         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D.
149         std::optional<double> volts{};
150         std::unique_ptr<PMBusWriteVoutCommandAction> action =
151             std::make_unique<PMBusWriteVoutCommandAction>(
152                 volts, pmbus_utils::VoutDataFormat::linear, -8, false);
153 
154         // Create mock I2CInterface.  Expect action to write 0x014D to
155         // VOUT_COMMAND (command/register 0x21).
156         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
157             std::make_unique<i2c::MockedI2CInterface>();
158         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
159         EXPECT_CALL(*i2cInterface,
160                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
161             .Times(1);
162 
163         // Create Configuration with volts value 1.3V
164         std::vector<std::unique_ptr<Action>> actions{};
165         actions.emplace_back(std::move(action));
166         std::unique_ptr<Configuration> configuration =
167             std::make_unique<Configuration>(1.3, std::move(actions));
168         Configuration* configurationPtr = configuration.get();
169 
170         // Create Device that contains Configuration
171         std::unique_ptr<PresenceDetection> presenceDetection{};
172         std::unique_ptr<Device> device = std::make_unique<Device>(
173             "vdd_reg", true,
174             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
175             std::move(i2cInterface), std::move(presenceDetection),
176             std::move(configuration));
177         Device* devicePtr = device.get();
178 
179         // Create Chassis that contains Device
180         std::vector<std::unique_ptr<Device>> devices{};
181         devices.emplace_back(std::move(device));
182         std::unique_ptr<Chassis> chassis =
183             std::make_unique<Chassis>(1, std::move(devices));
184         Chassis* chassisPtr = chassis.get();
185 
186         // Create System that contains Chassis
187         std::vector<std::unique_ptr<Rule>> rules{};
188         std::vector<std::unique_ptr<Chassis>> chassisVec{};
189         chassisVec.emplace_back(std::move(chassis));
190         System system{std::move(rules), std::move(chassisVec)};
191 
192         // Execute Configuration
193         configurationPtr->execute(services, system, *chassisPtr, *devicePtr);
194     }
195 
196     // Test where fails
197     {
198         // Create mock services.  Expect logDebug() and logError() to be called.
199         MockServices services{};
200         MockJournal& journal = services.getMockJournal();
201         std::vector<std::string> expectedErrMessagesException{
202             "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70",
203             "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: "
204             "0xFF }"};
205         EXPECT_CALL(journal, logDebug("Configuring vdd_reg")).Times(1);
206         EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1);
207         EXPECT_CALL(journal, logError("Unable to configure vdd_reg")).Times(1);
208 
209         // Create I2CWriteByteAction with register 0x7C and value 0x0A
210         std::unique_ptr<I2CWriteByteAction> action =
211             std::make_unique<I2CWriteByteAction>(0x7C, 0x0A);
212 
213         // Create mock I2CInterface.  write() throws an I2CException.
214         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
215             std::make_unique<i2c::MockedI2CInterface>();
216         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
217         EXPECT_CALL(*i2cInterface,
218                     write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A)))
219             .Times(1)
220             .WillOnce(Throw(
221                 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70}));
222 
223         // Create Configuration with no volts value specified
224         std::optional<double> volts{};
225         std::vector<std::unique_ptr<Action>> actions{};
226         actions.emplace_back(std::move(action));
227         std::unique_ptr<Configuration> configuration =
228             std::make_unique<Configuration>(volts, std::move(actions));
229         Configuration* configurationPtr = configuration.get();
230 
231         // Create Device that contains Configuration
232         std::unique_ptr<PresenceDetection> presenceDetection{};
233         std::unique_ptr<Device> device = std::make_unique<Device>(
234             "vdd_reg", true,
235             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
236             std::move(i2cInterface), std::move(presenceDetection),
237             std::move(configuration));
238         Device* devicePtr = device.get();
239 
240         // Create Chassis that contains Device
241         std::vector<std::unique_ptr<Device>> devices{};
242         devices.emplace_back(std::move(device));
243         std::unique_ptr<Chassis> chassis =
244             std::make_unique<Chassis>(1, std::move(devices));
245         Chassis* chassisPtr = chassis.get();
246 
247         // Create System that contains Chassis
248         std::vector<std::unique_ptr<Rule>> rules{};
249         std::vector<std::unique_ptr<Chassis>> chassisVec{};
250         chassisVec.emplace_back(std::move(chassis));
251         System system{std::move(rules), std::move(chassisVec)};
252 
253         // Execute Configuration
254         configurationPtr->execute(services, system, *chassisPtr, *devicePtr);
255     }
256 }
257 
258 // Test for execute(Services&, System&, Chassis&, Device&, Rail&)
259 TEST(ConfigurationTests, ExecuteForRail)
260 {
261     // Test where works: Volts value not specified
262     {
263         // Create mock services.  Expect logDebug() to be called.
264         MockServices services{};
265         MockJournal& journal = services.getMockJournal();
266         EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1);
267         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
268 
269         // Create I2CWriteByteAction with register 0x7C and value 0x0A
270         std::unique_ptr<I2CWriteByteAction> action =
271             std::make_unique<I2CWriteByteAction>(0x7C, 0x0A);
272 
273         // Create mock I2CInterface.  Expect action to write 0x0A to 0x7C.
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,
278                     write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A)))
279             .Times(1);
280 
281         // Create Configuration with no volts value specified
282         std::optional<double> volts{};
283         std::vector<std::unique_ptr<Action>> actions{};
284         actions.emplace_back(std::move(action));
285         std::unique_ptr<Configuration> configuration =
286             std::make_unique<Configuration>(volts, std::move(actions));
287         Configuration* configurationPtr = configuration.get();
288 
289         // Create Rail that contains Configuration
290         std::unique_ptr<Rail> rail =
291             std::make_unique<Rail>("vio2", std::move(configuration));
292         Rail* railPtr = rail.get();
293 
294         // Create Device that contains Rail
295         std::unique_ptr<PresenceDetection> presenceDetection{};
296         std::unique_ptr<Configuration> deviceConfiguration{};
297         std::vector<std::unique_ptr<Rail>> rails{};
298         rails.emplace_back(std::move(rail));
299         std::unique_ptr<Device> device = std::make_unique<Device>(
300             "reg1", true,
301             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
302             std::move(i2cInterface), std::move(presenceDetection),
303             std::move(deviceConfiguration), std::move(rails));
304         Device* devicePtr = device.get();
305 
306         // Create Chassis that contains Device
307         std::vector<std::unique_ptr<Device>> devices{};
308         devices.emplace_back(std::move(device));
309         std::unique_ptr<Chassis> chassis =
310             std::make_unique<Chassis>(1, std::move(devices));
311         Chassis* chassisPtr = chassis.get();
312 
313         // Create System that contains Chassis
314         std::vector<std::unique_ptr<Rule>> rules{};
315         std::vector<std::unique_ptr<Chassis>> chassisVec{};
316         chassisVec.emplace_back(std::move(chassis));
317         System system{std::move(rules), std::move(chassisVec)};
318 
319         // Execute Configuration
320         configurationPtr->execute(services, system, *chassisPtr, *devicePtr,
321                                   *railPtr);
322     }
323 
324     // Test where works: Volts value specified
325     {
326         // Create mock services.  Expect logDebug() to be called.
327         MockServices services{};
328         MockJournal& journal = services.getMockJournal();
329         EXPECT_CALL(journal, logDebug("Configuring vio2: volts=1.300000"))
330             .Times(1);
331         EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
332 
333         // Create PMBusWriteVoutCommandAction.  Do not specify a volts value
334         // because it will get a value of 1.3V from the
335         // ActionEnvironment/Configuration.  Specify a -8 exponent.
336         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D.
337         std::optional<double> volts{};
338         std::unique_ptr<PMBusWriteVoutCommandAction> action =
339             std::make_unique<PMBusWriteVoutCommandAction>(
340                 volts, pmbus_utils::VoutDataFormat::linear, -8, false);
341 
342         // Create mock I2CInterface.  Expect action to write 0x014D to
343         // VOUT_COMMAND (command/register 0x21).
344         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
345             std::make_unique<i2c::MockedI2CInterface>();
346         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
347         EXPECT_CALL(*i2cInterface,
348                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
349             .Times(1);
350 
351         // Create Configuration with volts value 1.3V
352         std::vector<std::unique_ptr<Action>> actions{};
353         actions.emplace_back(std::move(action));
354         std::unique_ptr<Configuration> configuration =
355             std::make_unique<Configuration>(1.3, std::move(actions));
356         Configuration* configurationPtr = configuration.get();
357 
358         // Create Rail that contains Configuration
359         std::unique_ptr<Rail> rail =
360             std::make_unique<Rail>("vio2", std::move(configuration));
361         Rail* railPtr = rail.get();
362 
363         // Create Device that contains Rail
364         std::unique_ptr<PresenceDetection> presenceDetection{};
365         std::unique_ptr<Configuration> deviceConfiguration{};
366         std::vector<std::unique_ptr<Rail>> rails{};
367         rails.emplace_back(std::move(rail));
368         std::unique_ptr<Device> device = std::make_unique<Device>(
369             "reg1", true,
370             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
371             std::move(i2cInterface), std::move(presenceDetection),
372             std::move(deviceConfiguration), std::move(rails));
373         Device* devicePtr = device.get();
374 
375         // Create Chassis that contains Device
376         std::vector<std::unique_ptr<Device>> devices{};
377         devices.emplace_back(std::move(device));
378         std::unique_ptr<Chassis> chassis =
379             std::make_unique<Chassis>(1, std::move(devices));
380         Chassis* chassisPtr = chassis.get();
381 
382         // Create System that contains Chassis
383         std::vector<std::unique_ptr<Rule>> rules{};
384         std::vector<std::unique_ptr<Chassis>> chassisVec{};
385         chassisVec.emplace_back(std::move(chassis));
386         System system{std::move(rules), std::move(chassisVec)};
387 
388         // Execute Configuration
389         configurationPtr->execute(services, system, *chassisPtr, *devicePtr,
390                                   *railPtr);
391     }
392 
393     // Test where fails
394     {
395         // Create mock services.  Expect logDebug() and logError() to be called.
396         MockServices services{};
397         MockJournal& journal = services.getMockJournal();
398         std::vector<std::string> expectedErrMessagesException{
399             "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70",
400             "ActionError: i2c_write_byte: { register: 0x7C, value: 0xA, mask: "
401             "0xFF }"};
402         EXPECT_CALL(journal, logDebug("Configuring vio2")).Times(1);
403         EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1);
404         EXPECT_CALL(journal, logError("Unable to configure vio2")).Times(1);
405 
406         // Create I2CWriteByteAction with register 0x7C and value 0x0A
407         std::unique_ptr<I2CWriteByteAction> action =
408             std::make_unique<I2CWriteByteAction>(0x7C, 0x0A);
409 
410         // Create mock I2CInterface.  write() throws an I2CException.
411         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
412             std::make_unique<i2c::MockedI2CInterface>();
413         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
414         EXPECT_CALL(*i2cInterface,
415                     write(TypedEq<uint8_t>(0x7C), TypedEq<uint8_t>(0x0A)))
416             .Times(1)
417             .WillOnce(Throw(
418                 i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70}));
419 
420         // Create Configuration with no volts value specified
421         std::optional<double> volts{};
422         std::vector<std::unique_ptr<Action>> actions{};
423         actions.emplace_back(std::move(action));
424         std::unique_ptr<Configuration> configuration =
425             std::make_unique<Configuration>(volts, std::move(actions));
426         Configuration* configurationPtr = configuration.get();
427 
428         // Create Rail that contains Configuration
429         std::unique_ptr<Rail> rail =
430             std::make_unique<Rail>("vio2", std::move(configuration));
431         Rail* railPtr = rail.get();
432 
433         // Create Device that contains Rail
434         std::unique_ptr<PresenceDetection> presenceDetection{};
435         std::unique_ptr<Configuration> deviceConfiguration{};
436         std::vector<std::unique_ptr<Rail>> rails{};
437         rails.emplace_back(std::move(rail));
438         std::unique_ptr<Device> device = std::make_unique<Device>(
439             "reg1", true,
440             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
441             std::move(i2cInterface), std::move(presenceDetection),
442             std::move(deviceConfiguration), std::move(rails));
443         Device* devicePtr = device.get();
444 
445         // Create Chassis that contains Device
446         std::vector<std::unique_ptr<Device>> devices{};
447         devices.emplace_back(std::move(device));
448         std::unique_ptr<Chassis> chassis =
449             std::make_unique<Chassis>(1, std::move(devices));
450         Chassis* chassisPtr = chassis.get();
451 
452         // Create System that contains Chassis
453         std::vector<std::unique_ptr<Rule>> rules{};
454         std::vector<std::unique_ptr<Chassis>> chassisVec{};
455         chassisVec.emplace_back(std::move(chassis));
456         System system{std::move(rules), std::move(chassisVec)};
457 
458         // Execute Configuration
459         configurationPtr->execute(services, system, *chassisPtr, *devicePtr,
460                                   *railPtr);
461     }
462 }
463 
464 TEST(ConfigurationTests, GetActions)
465 {
466     std::optional<double> volts{1.3};
467 
468     std::vector<std::unique_ptr<Action>> actions{};
469 
470     MockAction* action1 = new MockAction{};
471     actions.push_back(std::unique_ptr<MockAction>{action1});
472 
473     MockAction* action2 = new MockAction{};
474     actions.push_back(std::unique_ptr<MockAction>{action2});
475 
476     Configuration configuration(volts, std::move(actions));
477     EXPECT_EQ(configuration.getActions().size(), 2);
478     EXPECT_EQ(configuration.getActions()[0].get(), action1);
479     EXPECT_EQ(configuration.getActions()[1].get(), action2);
480 }
481 
482 TEST(ConfigurationTests, GetVolts)
483 {
484     // Test where volts value specified
485     {
486         std::optional<double> volts{3.2};
487 
488         std::vector<std::unique_ptr<Action>> actions{};
489         actions.push_back(std::make_unique<MockAction>());
490 
491         Configuration configuration(volts, std::move(actions));
492         EXPECT_EQ(configuration.getVolts().has_value(), true);
493         EXPECT_EQ(configuration.getVolts().value(), 3.2);
494     }
495 
496     // Test where volts value not specified
497     {
498         std::optional<double> volts{};
499 
500         std::vector<std::unique_ptr<Action>> actions{};
501         actions.push_back(std::make_unique<MockAction>());
502 
503         Configuration configuration(volts, std::move(actions));
504         EXPECT_EQ(configuration.getVolts().has_value(), false);
505     }
506 }
507