blob: a7ba37f36b8ba9ecb79866a5b5e969d894447538 [file] [log] [blame]
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -05001/**
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"
Shawn McCarney39765002020-04-09 18:03:26 -050017#include "chassis.hpp"
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050018#include "configuration.hpp"
Shawn McCarney39765002020-04-09 18:03:26 -050019#include "device.hpp"
20#include "i2c_interface.hpp"
21#include "i2c_write_byte_action.hpp"
22#include "journal.hpp"
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050023#include "mock_action.hpp"
Shawn McCarney39765002020-04-09 18:03:26 -050024#include "mock_journal.hpp"
25#include "mocked_i2c_interface.hpp"
26#include "pmbus_utils.hpp"
27#include "pmbus_write_vout_command_action.hpp"
Shawn McCarney779b9562020-04-13 17:05:45 -050028#include "presence_detection.hpp"
Shawn McCarney39765002020-04-09 18:03:26 -050029#include "rail.hpp"
Shawn McCarney779b9562020-04-13 17:05:45 -050030#include "rule.hpp"
Shawn McCarney39765002020-04-09 18:03:26 -050031#include "system.hpp"
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050032
Shawn McCarney779b9562020-04-13 17:05:45 -050033#include <cstdint>
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050034#include <memory>
35#include <optional>
36#include <utility>
37#include <vector>
38
Shawn McCarney39765002020-04-09 18:03:26 -050039#include <gmock/gmock.h>
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050040#include <gtest/gtest.h>
41
42using namespace phosphor::power::regulators;
Shawn McCarney39765002020-04-09 18:03:26 -050043using namespace phosphor::power::regulators::pmbus_utils;
44
45using ::testing::Return;
46using ::testing::Throw;
47using ::testing::TypedEq;
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050048
49TEST(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
Shawn McCarney39765002020-04-09 18:03:26 -050078// Test for execute(System&, Chassis&, Device&)
79TEST(ConfigurationTests, ExecuteForDevice)
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -050080{
Shawn McCarney39765002020-04-09 18:03:26 -050081 // 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&)
249TEST(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 }
Shawn McCarneyd3a8aab2020-03-10 10:30:02 -0500440}
441
442TEST(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
460TEST(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}