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