1 #include "../json_parser.hpp"
2 #include "../power_off_rule.hpp"
3 #include "mock_power_interface.hpp"
4 
5 #include <gtest/gtest.h>
6 
7 using namespace phosphor::fan::monitor;
8 using json = nlohmann::json;
9 
10 TEST(PowerOffRuleTest, TestRules)
11 {
12     PowerOffAction::PrePowerOffFunc func;
13     sd_event* event;
14     sd_event_default(&event);
15     sdeventplus::Event sdEvent{event};
16 
17     const auto faultConfig = R"(
18     {
19         "fault_handling":
20         {
21             "power_off_config": [
22                 {
23                     "type": "hard",
24                     "cause": "missing_fan_frus",
25                     "count": 2,
26                     "delay": 0,
27                     "state": "at_pgood"
28                 },
29                 {
30                     "type": "soft",
31                     "cause": "nonfunc_fan_rotors",
32                     "count": 3,
33                     "delay": 0,
34                     "state": "runtime"
35                 },
36                 {
37                     "type": "soft",
38                     "cause": "nonfunc_fan_rotors",
39                     "count": 4,
40                     "delay": 1,
41                     "state": "runtime"
42                 },
43                 {
44                     "type": "hard",
45                     "cause": "missing_fan_frus",
46                     "count": 4,
47                     "delay": 1,
48                     "state": "runtime"
49                 }
50             ]
51         }
52     })"_json;
53 
54     std::shared_ptr<PowerInterfaceBase> powerIface =
55         std::make_shared<MockPowerInterface>();
56 
57     MockPowerInterface& mockIface =
58         static_cast<MockPowerInterface&>(*powerIface);
59 
60     EXPECT_CALL(mockIface, hardPowerOff).Times(1);
61     EXPECT_CALL(mockIface, softPowerOff).Times(1);
62 
63     auto rules = getPowerOffRules(faultConfig, powerIface, func);
64     ASSERT_EQ(rules.size(), 4);
65 
66     FanHealth health{{"fan0", {false, {true, true}}},
67                      {"fan1", {false, {true, true}}}};
68 
69     {
70         // Check rule 0
71 
72         // wrong state, won't be active
73         rules[0]->check(PowerRuleState::runtime, health);
74         EXPECT_FALSE(rules[0]->active());
75 
76         rules[0]->check(PowerRuleState::atPgood, health);
77         EXPECT_TRUE(rules[0]->active());
78 
79         // Run the event loop, since the timeout is 0 it should
80         // run the power off and satisfy the EXPECT_CALL above
81         sdEvent.run(std::chrono::milliseconds(1));
82 
83         // It doesn't really make much sense to cancel a rule after it
84         // powered off, but it should at least say it isn't active.
85         rules[0]->cancel();
86         EXPECT_FALSE(rules[0]->active());
87     }
88 
89     {
90         // Check the second rule.
91         rules[1]->check(PowerRuleState::runtime, health);
92         EXPECT_FALSE(rules[1]->active());
93 
94         // > 2 nonfunc rotors
95         health["fan0"] = {true, {true, false}};
96         health["fan1"] = {true, {false, false}};
97 
98         rules[1]->check(PowerRuleState::runtime, health);
99         EXPECT_TRUE(rules[1]->active());
100 
101         // Run the event loop, since the timeout is 0 it should
102         // run the power off and satisfy the EXPECT_CALL above
103         sdEvent.run(std::chrono::milliseconds(1));
104     }
105 
106     {
107         // Check the third rule.  It has a timeout so long we can
108         // cancel it before it runs.
109         health["fan0"] = {true, {false, false}};
110         health["fan1"] = {true, {false, false}};
111 
112         rules[2]->check(PowerRuleState::runtime, health);
113         EXPECT_TRUE(rules[2]->active());
114 
115         sdEvent.run(std::chrono::milliseconds(1));
116 
117         rules[2]->cancel();
118         EXPECT_FALSE(rules[2]->active());
119 
120         // This will go past the timeout, it should have been canceled so the
121         // soft power off won't have run and the EXPECT_CALL above
122         // should be happy.
123         sdEvent.run(std::chrono::seconds(1));
124     }
125 
126     {
127         // Check the 4th rule. Resolve it before it completes
128         health["fan0"] = {false, {true, true}};
129         health["fan1"] = {false, {true, true}};
130         health["fan2"] = {false, {true, true}};
131         health["fan3"] = {false, {true, true}};
132 
133         rules[3]->check(PowerRuleState::runtime, health);
134         EXPECT_TRUE(rules[3]->active());
135 
136         // Won't complete yet
137         sdEvent.run(std::chrono::milliseconds(1));
138 
139         // Make them present
140         health["fan0"] = {true, {true, true}};
141         health["fan1"] = {true, {true, true}};
142         health["fan2"] = {true, {true, true}};
143         health["fan3"] = {true, {true, true}};
144 
145         //  It should be inactive now
146         rules[3]->check(PowerRuleState::runtime, health);
147         EXPECT_FALSE(rules[3]->active());
148     }
149 }
150