blob: d7fa5f3db15479957e41b53aae956c92463d696b [file] [log] [blame]
Shawn McCarney9284c302021-09-02 11:23:04 -05001/**
2 * Copyright © 2021 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
17#include "action.hpp"
18#include "chassis.hpp"
19#include "device.hpp"
20#include "i2c_capture_bytes_action.hpp"
21#include "i2c_compare_bit_action.hpp"
22#include "i2c_interface.hpp"
23#include "if_action.hpp"
24#include "log_phase_fault_action.hpp"
25#include "mock_action.hpp"
26#include "mock_error_logging.hpp"
27#include "mock_journal.hpp"
28#include "mock_services.hpp"
29#include "mocked_i2c_interface.hpp"
30#include "phase_fault.hpp"
31#include "phase_fault_detection.hpp"
32#include "rule.hpp"
33#include "system.hpp"
34
35#include <cstdint>
36#include <map>
37#include <memory>
38#include <stdexcept>
39#include <string>
40#include <utility>
41#include <vector>
42
43#include <gmock/gmock.h>
44#include <gtest/gtest.h>
45
46using namespace phosphor::power::regulators;
47
48using ::testing::_;
49using ::testing::A;
50using ::testing::NotNull;
51using ::testing::Ref;
52using ::testing::Return;
53using ::testing::SetArrayArgument;
54using ::testing::Throw;
55using ::testing::TypedEq;
56
57class PhaseFaultDetectionTests : public ::testing::Test
58{
59 public:
60 /**
61 * Constructor.
62 *
63 * Creates the following objects needed for calling the
64 * PhaseFaultDetection::execute() method:
65 * - Regulator Device
66 * - I/O expander Device
67 * - Chassis that contains the Devices
68 * - System that contains the Chassis
69 *
70 * Saves pointers to these objects in data members so they can be easily
71 * accessed in tests. Also saves pointers to the MockI2CInterface objects
72 * so they can be used in mock expectations.
73 */
74 PhaseFaultDetectionTests() : ::testing::Test{}
75 {
76 // Create mock I2CInterface for regulator Device and save pointer
77 auto regI2CInterface = std::make_unique<i2c::MockedI2CInterface>();
78 this->regI2CInterface = regI2CInterface.get();
79
80 // Create regulator Device and save pointer
81 auto regulator = std::make_unique<Device>(
82 "vdd1", true,
83 "/xyz/openbmc_project/inventory/system/chassis/motherboard/vdd1",
84 std::move(regI2CInterface));
85 this->regulator = regulator.get();
86
87 // Create mock I2CInterface for I/O expander Device and save pointer
88 auto ioExpI2CInterface = std::make_unique<i2c::MockedI2CInterface>();
89 this->ioExpI2CInterface = ioExpI2CInterface.get();
90
91 // Create I/O expander Device and save pointer
92 auto ioExpander = std::make_unique<Device>(
93 "ioexp1", false,
94 "/xyz/openbmc_project/inventory/system/chassis/motherboard/ioexp1",
95 std::move(ioExpI2CInterface));
96 this->ioExpander = ioExpander.get();
97
98 // Create Chassis that contains Devices and save pointer
99 std::vector<std::unique_ptr<Device>> devices{};
100 devices.emplace_back(std::move(regulator));
101 devices.emplace_back(std::move(ioExpander));
102 auto chassis = std::make_unique<Chassis>(
103 1, "/xyz/openbmc_project/inventory/system/chassis",
104 std::move(devices));
105 this->chassis = chassis.get();
106
107 // Create System that contains Chassis and save pointer
108 std::vector<std::unique_ptr<Rule>> rules{};
109 std::vector<std::unique_ptr<Chassis>> chassisVec{};
110 chassisVec.emplace_back(std::move(chassis));
Patrick Williams48781ae2023-05-10 07:50:50 -0500111 this->system = std::make_unique<System>(std::move(rules),
112 std::move(chassisVec));
Shawn McCarney9284c302021-09-02 11:23:04 -0500113 }
114
115 protected:
116 /**
117 * Note: The following pointers do NOT need to be explicitly deleted. They
118 * point to objects that are owned by the System object. All the objects
119 * will be automatically deleted.
120 */
121 i2c::MockedI2CInterface* regI2CInterface{nullptr};
122 Device* regulator{nullptr};
123 i2c::MockedI2CInterface* ioExpI2CInterface{nullptr};
124 Device* ioExpander{nullptr};
125 Chassis* chassis{nullptr};
126
127 /**
128 * System object. Owns all the other objects and will automatically delete
129 * them.
130 */
131 std::unique_ptr<System> system{};
132};
133
134TEST_F(PhaseFaultDetectionTests, Constructor)
135{
136 // Test where device ID not specified
137 {
138 std::vector<std::unique_ptr<Action>> actions{};
139 actions.push_back(std::make_unique<MockAction>());
140
141 PhaseFaultDetection detection{std::move(actions)};
142 EXPECT_EQ(detection.getActions().size(), 1);
143 EXPECT_EQ(detection.getDeviceID(), "");
144 }
145
146 // Test where device ID not specified
147 {
148 std::vector<std::unique_ptr<Action>> actions{};
149 actions.push_back(std::make_unique<MockAction>());
150 actions.push_back(std::make_unique<MockAction>());
151
152 PhaseFaultDetection detection{std::move(actions), "ioexp1"};
153 EXPECT_EQ(detection.getActions().size(), 2);
154 EXPECT_EQ(detection.getDeviceID(), "ioexp1");
155 }
156}
157
158TEST_F(PhaseFaultDetectionTests, ClearErrorHistory)
159{
160 std::vector<std::unique_ptr<Action>> actions{};
161
162 // Create MockAction that will switch every 5 times between working and
163 // throwing an exception. Expect it to be executed 20 times.
164 std::logic_error error{"Logic error"};
165 auto action = std::make_unique<MockAction>();
166 EXPECT_CALL(*action, execute)
167 .Times(20)
168 .WillOnce(Return(true))
169 .WillOnce(Return(true))
170 .WillOnce(Return(true))
171 .WillOnce(Return(true))
172 .WillOnce(Return(true))
173 .WillOnce(Throw(error))
174 .WillOnce(Throw(error))
175 .WillOnce(Throw(error))
176 .WillOnce(Throw(error))
177 .WillOnce(Throw(error))
178 .WillOnce(Return(true))
179 .WillOnce(Return(true))
180 .WillOnce(Return(true))
181 .WillOnce(Return(true))
182 .WillOnce(Return(true))
183 .WillOnce(Throw(error))
184 .WillOnce(Throw(error))
185 .WillOnce(Throw(error))
186 .WillOnce(Throw(error))
187 .WillOnce(Throw(error));
188 actions.push_back(std::move(action));
189
190 // Create a LogPhaseFaultAction that will log N faults
191 actions.push_back(std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n));
192
193 // Create a LogPhaseFaultAction that will log N+1 faults
194 actions.push_back(
195 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1));
196
197 // Create PhaseFaultDetection
198 PhaseFaultDetection detection{std::move(actions)};
199
200 // Create lambda that sets Journal and ErrorLogging expectations when
201 // performing phase fault detection 10 times. The lambda allows us to
202 // set the same expectations twice without duplicate code.
203 auto setExpectations = [](MockServices& services) {
204 // Set Journal service expectations:
205 // - 3 error messages for the MockAction exceptions
206 // - 3 error messages for inability to detect phase faults
207 // - 2 error messages for the N phase fault
208 // - 2 error messages for the N+1 phase fault
209 MockJournal& journal = services.getMockJournal();
210 EXPECT_CALL(journal, logError(std::vector<std::string>{"Logic error"}))
211 .Times(3);
212 EXPECT_CALL(journal,
213 logError("Unable to detect phase faults in regulator vdd1"))
214 .Times(3);
215 EXPECT_CALL(
216 journal,
217 logError("n phase fault detected in regulator vdd1: count=1"))
218 .Times(1);
219 EXPECT_CALL(
220 journal,
221 logError("n phase fault detected in regulator vdd1: count=2"))
222 .Times(1);
223 EXPECT_CALL(
224 journal,
225 logError("n+1 phase fault detected in regulator vdd1: count=1"))
226 .Times(1);
227 EXPECT_CALL(
228 journal,
229 logError("n+1 phase fault detected in regulator vdd1: count=2"))
230 .Times(1);
231
232 // Set ErrorLogging service expectations:
233 // - Internal error should be logged once for the MockAction exceptions
234 // - N phase fault error should be logged once
235 // - N+1 phase fault error should be logged once
236 MockErrorLogging& errorLogging = services.getMockErrorLogging();
237 EXPECT_CALL(errorLogging, logInternalError).Times(1);
238 EXPECT_CALL(errorLogging, logPhaseFault(_, _, PhaseFaultType::n, _, _))
239 .Times(1);
240 EXPECT_CALL(errorLogging,
241 logPhaseFault(_, _, PhaseFaultType::n_plus_1, _, _))
242 .Times(1);
243 };
244
245 // Perform phase fault detection 10 times to set error history data members
246 {
247 // Create mock services. Set expectations via lambda.
248 MockServices services{};
249 setExpectations(services);
250
251 // Execute PhaseFaultDetection 10 times
252 for (int i = 1; i <= 10; ++i)
253 {
254 detection.execute(services, *system, *chassis, *regulator);
255 }
256 }
257
258 // Clear error history
259 detection.clearErrorHistory();
260
261 // Perform phase fault detection 10 more times. Verify errors logged again.
262 {
263 // Create mock services. Set expectations via lambda.
264 MockServices services{};
265 setExpectations(services);
266
267 // Execute PhaseFaultDetection 10 times
268 for (int i = 1; i <= 10; ++i)
269 {
270 detection.execute(services, *system, *chassis, *regulator);
271 }
272 }
273}
274
275TEST_F(PhaseFaultDetectionTests, Execute)
276{
277 // Test where Device ID was specified
278 {
279 // Create I2CCompareBitAction that will use an I2CInterface
280 auto action = std::make_unique<I2CCompareBitAction>(0x1C, 2, 0);
281
282 // Create PhaseFaultDetection. Specify device ID of I/O expander.
283 std::vector<std::unique_ptr<Action>> actions{};
284 actions.push_back(std::move(action));
285 PhaseFaultDetection detection{std::move(actions), "ioexp1"};
286
287 // Set expectations for regulator I2C interface. Should not be used.
288 EXPECT_CALL(*regI2CInterface, isOpen).Times(0);
289 EXPECT_CALL(*regI2CInterface, read(0x1C, A<uint8_t&>())).Times(0);
290
291 // Set expectations for I/O expander I2C interface. Should be used.
292 EXPECT_CALL(*ioExpI2CInterface, isOpen).Times(1).WillOnce(Return(true));
293 EXPECT_CALL(*ioExpI2CInterface, read(0x1C, A<uint8_t&>())).Times(1);
294
295 // Create mock services. Expect no errors to be logged.
296 MockServices services{};
297 MockJournal& journal = services.getMockJournal();
298 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
299 MockErrorLogging& errorLogging = services.getMockErrorLogging();
300 EXPECT_CALL(errorLogging, logPhaseFault).Times(0);
301
302 // Execute PhaseFaultDetection
303 detection.execute(services, *system, *chassis, *regulator);
304 }
305
306 // Test where Device ID was not specified
307 {
308 // Create I2CCompareBitAction that will use an I2CInterface
309 auto action = std::make_unique<I2CCompareBitAction>(0x1C, 2, 0);
310
311 // Create PhaseFaultDetection. Specify no device ID, which means the
312 // regulator should be used.
313 std::vector<std::unique_ptr<Action>> actions{};
314 actions.push_back(std::move(action));
315 PhaseFaultDetection detection{std::move(actions)};
316
317 // Set expectations for regulator I2C interface. Should be used.
318 EXPECT_CALL(*regI2CInterface, isOpen).Times(1).WillOnce(Return(true));
319 EXPECT_CALL(*regI2CInterface, read(0x1C, A<uint8_t&>())).Times(1);
320
321 // Set expectations for I/O expander I2C interface. Should not be used.
322 EXPECT_CALL(*ioExpI2CInterface, isOpen).Times(0);
323 EXPECT_CALL(*ioExpI2CInterface, read(0x1C, A<uint8_t&>())).Times(0);
324
325 // Create mock services. Expect no errors to be logged.
326 MockServices services{};
327 MockJournal& journal = services.getMockJournal();
328 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
329 MockErrorLogging& errorLogging = services.getMockErrorLogging();
330 EXPECT_CALL(errorLogging, logPhaseFault).Times(0);
331
332 // Execute PhaseFaultDetection
333 detection.execute(services, *system, *chassis, *regulator);
334 }
335
336 // Test where no phase faults detected
337 {
338 // Create MockAction. Expect it to be executed 3 times.
339 auto action = std::make_unique<MockAction>();
340 EXPECT_CALL(*action, execute).Times(3).WillRepeatedly(Return(true));
341
342 // Create PhaseFaultDetection
343 std::vector<std::unique_ptr<Action>> actions{};
344 actions.push_back(std::move(action));
345 PhaseFaultDetection detection{std::move(actions)};
346
347 // Create mock services. Expect no errors to be logged.
348 MockServices services{};
349 MockJournal& journal = services.getMockJournal();
350 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
351 MockErrorLogging& errorLogging = services.getMockErrorLogging();
352 EXPECT_CALL(errorLogging, logPhaseFault).Times(0);
353
354 // Execute PhaseFaultDetection 3 times
355 for (int i = 1; i <= 3; ++i)
356 {
357 detection.execute(services, *system, *chassis, *regulator);
358 }
359 }
360
361 // Test where N fault occurs, but not twice in a row
362 {
363 // Create MockAction that will alternate between returning true and
364 // false. Expect it to be executed 6 times. Use it for the "condition"
365 // of an IfAction.
366 auto conditionAction = std::make_unique<MockAction>();
367 EXPECT_CALL(*conditionAction, execute)
368 .Times(6)
369 .WillOnce(Return(true))
370 .WillOnce(Return(false))
371 .WillOnce(Return(true))
372 .WillOnce(Return(false))
373 .WillOnce(Return(true))
374 .WillOnce(Return(false));
375
376 // Create a LogPhaseFaultAction that will log an N phase fault in the
377 // ActionEnvironment. Use it for the "then" clause of an IfAction.
378 auto logPhaseFaultAction =
379 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n);
380
381 // Create an IfAction that will log an N phase fault in the
382 // ActionEnvironment if the mock condition is true.
383 std::vector<std::unique_ptr<Action>> thenActions{};
384 thenActions.push_back(std::move(logPhaseFaultAction));
385 auto ifAction = std::make_unique<IfAction>(std::move(conditionAction),
386 std::move(thenActions));
387
388 // Create PhaseFaultDetection
389 std::vector<std::unique_ptr<Action>> actions{};
390 actions.push_back(std::move(ifAction));
391 PhaseFaultDetection detection{std::move(actions)};
392
393 // Create mock services. Expect 3 error messages in the journal for
394 // an N phase fault detected with the consecutive count = 1. Expect no
395 // phase fault error to be logged.
396 MockServices services{};
397 MockJournal& journal = services.getMockJournal();
398 EXPECT_CALL(
399 journal,
400 logError("n phase fault detected in regulator vdd1: count=1"))
401 .Times(3);
402 EXPECT_CALL(
403 journal,
404 logError("n phase fault detected in regulator vdd1: count=2"))
405 .Times(0);
406 MockErrorLogging& errorLogging = services.getMockErrorLogging();
407 EXPECT_CALL(errorLogging, logPhaseFault).Times(0);
408
409 // Execute PhaseFaultDetection 6 times
410 for (int i = 1; i <= 6; ++i)
411 {
412 detection.execute(services, *system, *chassis, *regulator);
413 }
414 }
415
416 // Test where N+1 fault occurs, but not twice in a row
417 {
418 // Create MockAction that will alternate between returning true and
419 // false. Expect it to be executed 6 times. Use it for the "condition"
420 // of an IfAction.
421 auto conditionAction = std::make_unique<MockAction>();
422 EXPECT_CALL(*conditionAction, execute)
423 .Times(6)
424 .WillOnce(Return(true))
425 .WillOnce(Return(false))
426 .WillOnce(Return(true))
427 .WillOnce(Return(false))
428 .WillOnce(Return(true))
429 .WillOnce(Return(false));
430
431 // Create a LogPhaseFaultAction that will log an N+1 phase fault in the
432 // ActionEnvironment. Use it for the "then" clause of an IfAction.
433 auto logPhaseFaultAction =
434 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1);
435
436 // Create an IfAction that will log an N+1 phase fault in the
437 // ActionEnvironment if the mock condition is true.
438 std::vector<std::unique_ptr<Action>> thenActions{};
439 thenActions.push_back(std::move(logPhaseFaultAction));
440 auto ifAction = std::make_unique<IfAction>(std::move(conditionAction),
441 std::move(thenActions));
442
443 // Create PhaseFaultDetection
444 std::vector<std::unique_ptr<Action>> actions{};
445 actions.push_back(std::move(ifAction));
446 PhaseFaultDetection detection{std::move(actions)};
447
448 // Create mock services. Expect 3 error messages in the journal for
449 // an N+1 phase fault detected with the consecutive count = 1. Expect
450 // no phase fault error to be logged.
451 MockServices services{};
452 MockJournal& journal = services.getMockJournal();
453 EXPECT_CALL(
454 journal,
455 logError("n+1 phase fault detected in regulator vdd1: count=1"))
456 .Times(3);
457 EXPECT_CALL(
458 journal,
459 logError("n+1 phase fault detected in regulator vdd1: count=2"))
460 .Times(0);
461 MockErrorLogging& errorLogging = services.getMockErrorLogging();
462 EXPECT_CALL(errorLogging, logPhaseFault).Times(0);
463
464 // Execute PhaseFaultDetection 6 times
465 for (int i = 1; i <= 6; ++i)
466 {
467 detection.execute(services, *system, *chassis, *regulator);
468 }
469 }
470
471 // Test where N fault detected twice in a row
472 {
473 // Create action that will log an N phase fault in ActionEnvironment
474 auto action = std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n);
475
476 // Create PhaseFaultDetection
477 std::vector<std::unique_ptr<Action>> actions{};
478 actions.push_back(std::move(action));
479 PhaseFaultDetection detection{std::move(actions)};
480
481 // Create mock services with the following expectations:
482 // - 2 error messages in journal for N phase fault detected
483 // - 0 error messages in journal for N+1 phase fault detected
484 // - 1 N phase fault error logged
485 // - 0 N+1 phase fault errors logged
486 MockServices services{};
487 MockJournal& journal = services.getMockJournal();
488 EXPECT_CALL(
489 journal,
490 logError("n phase fault detected in regulator vdd1: count=1"))
491 .Times(1);
492 EXPECT_CALL(
493 journal,
494 logError("n phase fault detected in regulator vdd1: count=2"))
495 .Times(1);
496 EXPECT_CALL(
497 journal,
498 logError("n+1 phase fault detected in regulator vdd1: count=1"))
499 .Times(0);
500 MockErrorLogging& errorLogging = services.getMockErrorLogging();
501 std::map<std::string, std::string> additionalData{};
502 EXPECT_CALL(errorLogging,
503 logPhaseFault(Entry::Level::Warning, Ref(journal),
504 PhaseFaultType::n, regulator->getFRU(),
505 additionalData))
506 .Times(1);
507 EXPECT_CALL(errorLogging,
508 logPhaseFault(_, _, PhaseFaultType::n_plus_1, _, _))
509 .Times(0);
510
511 // Execute PhaseFaultDetection 5 times
512 for (int i = 1; i <= 5; ++i)
513 {
514 detection.execute(services, *system, *chassis, *regulator);
515 }
516 }
517
518 // Test where N+1 fault detected twice in a row
519 {
520 // Create action that will log an N+1 phase fault in ActionEnvironment
521 auto action =
522 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1);
523
524 // Create PhaseFaultDetection
525 std::vector<std::unique_ptr<Action>> actions{};
526 actions.push_back(std::move(action));
527 PhaseFaultDetection detection{std::move(actions)};
528
529 // Create mock services with the following expectations:
530 // - 2 error messages in journal for N+1 phase fault detected
531 // - 0 error messages in journal for N phase fault detected
532 // - 1 N+1 phase fault error logged
533 // - 0 N phase fault errors logged
534 MockServices services{};
535 MockJournal& journal = services.getMockJournal();
536 EXPECT_CALL(
537 journal,
538 logError("n+1 phase fault detected in regulator vdd1: count=1"))
539 .Times(1);
540 EXPECT_CALL(
541 journal,
542 logError("n+1 phase fault detected in regulator vdd1: count=2"))
543 .Times(1);
544 EXPECT_CALL(
545 journal,
546 logError("n phase fault detected in regulator vdd1: count=1"))
547 .Times(0);
548 MockErrorLogging& errorLogging = services.getMockErrorLogging();
549 std::map<std::string, std::string> additionalData{};
550 EXPECT_CALL(errorLogging,
551 logPhaseFault(Entry::Level::Informational, Ref(journal),
552 PhaseFaultType::n_plus_1, regulator->getFRU(),
553 additionalData))
554 .Times(1);
555 EXPECT_CALL(errorLogging, logPhaseFault(_, _, PhaseFaultType::n, _, _))
556 .Times(0);
557
558 // Execute PhaseFaultDetection 5 times
559 for (int i = 1; i <= 5; ++i)
560 {
561 detection.execute(services, *system, *chassis, *regulator);
562 }
563 }
564
565 // Test where both faults detected twice in a row
566 {
567 std::vector<std::unique_ptr<Action>> actions{};
568
569 // Create action that will log an N+1 phase fault in ActionEnvironment
570 actions.push_back(
571 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n_plus_1));
572
573 // Create action that will log an N phase fault in ActionEnvironment
574 actions.push_back(
575 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n));
576
577 // Create PhaseFaultDetection
578 PhaseFaultDetection detection{std::move(actions)};
579
580 // Create mock services with the following expectations:
581 // - 2 error messages in journal for N+1 phase fault detected
582 // - 2 error messages in journal for N phase fault detected
583 // - 1 N+1 phase fault error logged
584 // - 1 N phase fault error logged
585 MockServices services{};
586 MockJournal& journal = services.getMockJournal();
587 EXPECT_CALL(
588 journal,
589 logError("n+1 phase fault detected in regulator vdd1: count=1"))
590 .Times(1);
591 EXPECT_CALL(
592 journal,
593 logError("n+1 phase fault detected in regulator vdd1: count=2"))
594 .Times(1);
595 EXPECT_CALL(
596 journal,
597 logError("n phase fault detected in regulator vdd1: count=1"))
598 .Times(1);
599 EXPECT_CALL(
600 journal,
601 logError("n phase fault detected in regulator vdd1: count=2"))
602 .Times(1);
603 MockErrorLogging& errorLogging = services.getMockErrorLogging();
604 std::map<std::string, std::string> additionalData{};
605 EXPECT_CALL(errorLogging,
606 logPhaseFault(Entry::Level::Informational, Ref(journal),
607 PhaseFaultType::n_plus_1, regulator->getFRU(),
608 additionalData))
609 .Times(1);
610 EXPECT_CALL(errorLogging,
611 logPhaseFault(Entry::Level::Warning, Ref(journal),
612 PhaseFaultType::n, regulator->getFRU(),
613 additionalData))
614 .Times(1);
615
616 // Execute PhaseFaultDetection 5 times
617 for (int i = 1; i <= 5; ++i)
618 {
619 detection.execute(services, *system, *chassis, *regulator);
620 }
621 }
622
623 // Test where additional error data is captured
624 {
625 std::vector<std::unique_ptr<Action>> actions{};
626
627 // Create action that will capture 1 byte from register 0x0F
628 actions.push_back(std::make_unique<I2CCaptureBytesAction>(0x0F, 1));
629
630 // Create action that will capture 2 bytes from register 0x21
631 actions.push_back(std::make_unique<I2CCaptureBytesAction>(0x21, 2));
632
633 // Create action that will log an N phase fault in ActionEnvironment
634 actions.push_back(
635 std::make_unique<LogPhaseFaultAction>(PhaseFaultType::n));
636
637 // Create PhaseFaultDetection
638 PhaseFaultDetection detection{std::move(actions)};
639
640 // Set expectations for regulator I2C interface:
641 // - isOpen() will return true
642 // - reading 1 byte from register 0x0F will return 0xDA
643 // - reading 2 bytes from register 0x21 will return [ 0x56, 0x14 ]
644 EXPECT_CALL(*regI2CInterface, isOpen).WillRepeatedly(Return(true));
645 uint8_t register0FValues[] = {0xDA};
646 EXPECT_CALL(*regI2CInterface,
647 read(0x0F, TypedEq<uint8_t&>(1), NotNull(),
648 i2c::I2CInterface::Mode::I2C))
649 .Times(5)
650 .WillRepeatedly(
651 SetArrayArgument<2>(register0FValues, register0FValues + 1));
652 uint8_t register21Values[] = {0x56, 0x14};
653 EXPECT_CALL(*regI2CInterface,
654 read(0x21, TypedEq<uint8_t&>(2), NotNull(),
655 i2c::I2CInterface::Mode::I2C))
656 .Times(5)
657 .WillRepeatedly(
658 SetArrayArgument<2>(register21Values, register21Values + 2));
659
660 // Create mock services with the following expectations:
661 // - 2 error messages in journal for N phase fault detected
662 // - 1 N phase fault error logged with additional data
663 MockServices services{};
664 MockJournal& journal = services.getMockJournal();
665 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(2);
666 MockErrorLogging& errorLogging = services.getMockErrorLogging();
667 std::map<std::string, std::string> additionalData{
668 {"vdd1_register_0xF", "[ 0xDA ]"},
669 {"vdd1_register_0x21", "[ 0x56, 0x14 ]"}};
670 EXPECT_CALL(errorLogging,
671 logPhaseFault(Entry::Level::Warning, Ref(journal),
672 PhaseFaultType::n, regulator->getFRU(),
673 additionalData))
674 .Times(1);
675
676 // Execute PhaseFaultDetection 5 times
677 for (int i = 1; i <= 5; ++i)
678 {
679 detection.execute(services, *system, *chassis, *regulator);
680 }
681 }
682
683 // Test where fails: Exception thrown by actions
684 {
685 // Create I2CCompareBitAction that will use the I2CInterface
686 auto action = std::make_unique<I2CCompareBitAction>(0x7C, 2, 0);
687
688 // Create PhaseFaultDetection
689 std::vector<std::unique_ptr<Action>> actions{};
690 actions.push_back(std::move(action));
691 PhaseFaultDetection detection{std::move(actions)};
692
693 // Set expectations for regulator I2C interface:
694 // - isOpen() will return true
695 // - reading 1 byte from register 0x7C will throw an I2CException
696 EXPECT_CALL(*regI2CInterface, isOpen).WillRepeatedly(Return(true));
697 EXPECT_CALL(*regI2CInterface, read(0x7C, A<uint8_t&>()))
698 .Times(5)
699 .WillRepeatedly(Throw(
700 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
701
702 // Create mock services with the following expectations:
703 // - 3 error messages in journal for exception
704 // - 3 error messages in journal for inability to detect phase faults
705 // - 1 I2C error logged
706 MockServices services{};
707 MockJournal& journal = services.getMockJournal();
708 std::vector<std::string> exceptionMessages{
709 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70",
710 "ActionError: i2c_compare_bit: { register: 0x7C, position: 2, "
711 "value: 0 }"};
712 EXPECT_CALL(journal, logError(exceptionMessages)).Times(3);
713 EXPECT_CALL(journal,
714 logError("Unable to detect phase faults in regulator vdd1"))
715 .Times(3);
716 MockErrorLogging& errorLogging = services.getMockErrorLogging();
717 EXPECT_CALL(errorLogging,
718 logI2CError(Entry::Level::Warning, Ref(journal),
719 "/dev/i2c-1", 0x70, 0))
720 .Times(1);
721
722 // Execute PhaseFaultDetection 5 times
723 for (int i = 1; i <= 5; ++i)
724 {
725 detection.execute(services, *system, *chassis, *regulator);
726 }
727 }
728}
729
730TEST_F(PhaseFaultDetectionTests, GetActions)
731{
732 std::vector<std::unique_ptr<Action>> actions{};
733
734 MockAction* action1 = new MockAction{};
735 actions.push_back(std::unique_ptr<MockAction>{action1});
736
737 MockAction* action2 = new MockAction{};
738 actions.push_back(std::unique_ptr<MockAction>{action2});
739
740 PhaseFaultDetection detection{std::move(actions)};
741 EXPECT_EQ(detection.getActions().size(), 2);
742 EXPECT_EQ(detection.getActions()[0].get(), action1);
743 EXPECT_EQ(detection.getActions()[1].get(), action2);
744}
745
746TEST_F(PhaseFaultDetectionTests, GetDeviceID)
747{
748 // Test where device ID not specified
749 {
750 std::vector<std::unique_ptr<Action>> actions{};
751 actions.push_back(std::make_unique<MockAction>());
752
753 PhaseFaultDetection detection{std::move(actions)};
754 EXPECT_EQ(detection.getDeviceID(), "");
755 }
756
757 // Test where device ID not specified
758 {
759 std::vector<std::unique_ptr<Action>> actions{};
760 actions.push_back(std::make_unique<MockAction>());
761
762 PhaseFaultDetection detection{std::move(actions), "ioexp1"};
763 EXPECT_EQ(detection.getDeviceID(), "ioexp1");
764 }
765}