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