1 /**
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 
46 using namespace phosphor::power::regulators;
47 
48 using ::testing::_;
49 using ::testing::A;
50 using ::testing::NotNull;
51 using ::testing::Ref;
52 using ::testing::Return;
53 using ::testing::SetArrayArgument;
54 using ::testing::Throw;
55 using ::testing::TypedEq;
56 
57 class 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));
111         this->system =
112             std::make_unique<System>(std::move(rules), std::move(chassisVec));
113     }
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 
134 TEST_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 
158 TEST_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 
275 TEST_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 
730 TEST_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 
746 TEST_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 }
766