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 "extensions/openpower-pels/service_indicators.hpp"
17 #include "mocks.hpp"
18 #include "pel_utils.hpp"
19 
20 #include <gtest/gtest.h>
21 
22 using namespace openpower::pels;
23 using CalloutVector = std::vector<std::unique_ptr<src::Callout>>;
24 using ::testing::_;
25 using ::testing::Return;
26 using ::testing::Throw;
27 
28 // Test the ignore() function works
29 TEST(ServiceIndicatorsTest, IgnoreTest)
30 {
31     MockDataInterface dataIface;
32     service_indicators::LightPath lightPath{dataIface};
33 
34     // PEL must have serviceable action flag set and be created
35     // by the BMC or Hostboot.
36     std::vector<std::tuple<char, uint16_t, bool>> testParams{
37         {'O', 0xA400, false}, // BMC serviceable, don't ignore
38         {'B', 0xA400, false}, // Hostboot serviceable, don't ignore
39         {'H', 0xA400, true},  // PHYP serviceable, ignore
40         {'O', 0x2400, true},  // BMC not serviceable, ignore
41         {'B', 0x2400, true},  // Hostboot not serviceable, ignore
42         {'H', 0x2400, true},  // PHYP not serviceable, ignore
43     };
44 
45     for (const auto& test : testParams)
46     {
47         auto data = pelFactory(1, std::get<char>(test), 0x20,
48                                std::get<uint16_t>(test), 500);
49         PEL pel{data};
50 
51         EXPECT_EQ(lightPath.ignore(pel), std::get<bool>(test));
52     }
53 }
54 
55 // Test that only high, medium, and medium group A hardware
56 // callouts have their location codes extracted.
57 TEST(ServiceIndicatorsTest, OneCalloutPriorityTest)
58 {
59     MockDataInterface dataIface;
60     service_indicators::LightPath lightPath{dataIface};
61 
62     // The priorities to test, with the expected getLocationCodes results.
63     std::vector<std::tuple<CalloutPriority, std::vector<std::string>>>
64         testCallouts{{CalloutPriority::high, {"U27-P1"}},
65                      {CalloutPriority::medium, {"U27-P1"}},
66                      {CalloutPriority::mediumGroupA, {"U27-P1"}},
67                      {CalloutPriority::mediumGroupB, {}},
68                      {CalloutPriority::mediumGroupC, {}},
69                      {CalloutPriority::low, {}}};
70 
71     for (const auto& test : testCallouts)
72     {
73         auto callout = std::make_unique<src::Callout>(
74             std::get<CalloutPriority>(test), "U27-P1", "1234567", "aaaa",
75             "123456789ABC");
76 
77         CalloutVector callouts;
78         callouts.push_back(std::move(callout));
79 
80         EXPECT_EQ(lightPath.getLocationCodes(callouts),
81                   std::get<std::vector<std::string>>(test));
82     }
83 }
84 
85 // Test that only normal hardware callouts and symbolic FRU
86 // callouts with trusted location codes have their location
87 // codes extracted.
88 TEST(ServiceIndicatorsTest, OneCalloutTypeTest)
89 {
90     MockDataInterface dataIface;
91     service_indicators::LightPath lightPath{dataIface};
92 
93     // Regular hardware callout
94     {
95         CalloutVector callouts;
96         callouts.push_back(
97             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P1",
98                                            "1234567", "aaaa", "123456789ABC"));
99 
100         EXPECT_EQ(lightPath.getLocationCodes(callouts),
101                   std::vector<std::string>{"U27-P1"});
102     }
103 
104     // Symbolic FRU with trusted loc code callout
105     {
106         CalloutVector callouts;
107         callouts.push_back(std::make_unique<src::Callout>(
108             CalloutPriority::high, "service_docs", "U27-P1", true));
109 
110         EXPECT_EQ(lightPath.getLocationCodes(callouts),
111                   std::vector<std::string>{"U27-P1"});
112     }
113 
114     // Symbolic FRU without trusted loc code callout
115     {
116         CalloutVector callouts;
117         callouts.push_back(std::make_unique<src::Callout>(
118             CalloutPriority::high, "service_docs", "U27-P1", false));
119 
120         EXPECT_EQ(lightPath.getLocationCodes(callouts),
121                   std::vector<std::string>{});
122     }
123 
124     // Procedure callout
125     {
126         CalloutVector callouts;
127         callouts.push_back(
128             std::make_unique<src::Callout>(CalloutPriority::high, "bmc_code"));
129 
130         EXPECT_EQ(lightPath.getLocationCodes(callouts),
131                   std::vector<std::string>{});
132     }
133 }
134 
135 // Test that only the callouts in the first group have their location
136 // codes extracted, where a group is one or more callouts listed
137 // together with priorities of high, medium, or medium group A
138 // (and medium is only ever contains 1 item).
139 TEST(ServiceIndicatorsTest, CalloutGroupingTest)
140 {
141     MockDataInterface dataIface;
142     service_indicators::LightPath lightPath{dataIface};
143 
144     // high/high/medium/high just grabs the first 2
145     {
146         CalloutVector callouts;
147         callouts.push_back(
148             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P1",
149                                            "1234567", "aaaa", "123456789ABC"));
150         callouts.push_back(
151             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P2",
152                                            "1234567", "aaaa", "123456789ABC"));
153         callouts.push_back(
154             std::make_unique<src::Callout>(CalloutPriority::medium, "U27-P3",
155                                            "1234567", "aaaa", "123456789ABC"));
156         // This high priority callout after a medium isn't actually valid, since
157         // callouts are sorted, but test it anyway.
158         callouts.push_back(
159             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P4",
160                                            "1234567", "aaaa", "123456789ABC"));
161 
162         EXPECT_EQ(lightPath.getLocationCodes(callouts),
163                   (std::vector<std::string>{"U27-P1", "U27-P2"}));
164     }
165 
166     // medium/medium just grabs the first medium
167     {
168         CalloutVector callouts;
169         callouts.push_back(
170             std::make_unique<src::Callout>(CalloutPriority::medium, "U27-P1",
171                                            "1234567", "aaaa", "123456789ABC"));
172         callouts.push_back(
173             std::make_unique<src::Callout>(CalloutPriority::medium, "U27-P2",
174                                            "1234567", "aaaa", "123456789ABC"));
175 
176         EXPECT_EQ(lightPath.getLocationCodes(callouts),
177                   std::vector<std::string>{"U27-P1"});
178     }
179 
180     // mediumA/mediumA/medium just grabs the first 2
181     {
182         CalloutVector callouts;
183         callouts.push_back(std::make_unique<src::Callout>(
184             CalloutPriority::mediumGroupA, "U27-P1", "1234567", "aaaa",
185             "123456789ABC"));
186 
187         callouts.push_back(std::make_unique<src::Callout>(
188             CalloutPriority::mediumGroupA, "U27-P2", "1234567", "aaaa",
189             "123456789ABC"));
190 
191         callouts.push_back(
192             std::make_unique<src::Callout>(CalloutPriority::medium, "U27-P3",
193                                            "1234567", "aaaa", "123456789ABC"));
194 
195         EXPECT_EQ(lightPath.getLocationCodes(callouts),
196                   (std::vector<std::string>{"U27-P1", "U27-P2"}));
197     }
198 }
199 
200 // Test that if any callouts in group are not HW/trusted symbolic
201 // FRU callouts then no location codes will be extracted
202 TEST(ServiceIndicatorsTest, CalloutMixedTypesTest)
203 {
204     MockDataInterface dataIface;
205     service_indicators::LightPath lightPath{dataIface};
206 
207     // Mixing FRU with trusted symbolic FRU is OK
208     {
209         CalloutVector callouts;
210         callouts.push_back(
211             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P1",
212                                            "1234567", "aaaa", "123456789ABC"));
213         callouts.push_back(std::make_unique<src::Callout>(
214             CalloutPriority::high, "service_docs", "U27-P2", true));
215 
216         EXPECT_EQ(lightPath.getLocationCodes(callouts),
217                   (std::vector<std::string>{"U27-P1", "U27-P2"}));
218     }
219 
220     // Normal FRU callout with a non-trusted symbolic FRU callout not OK
221     {
222         CalloutVector callouts;
223         callouts.push_back(
224             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P1",
225                                            "1234567", "aaaa", "123456789ABC"));
226         callouts.push_back(std::make_unique<src::Callout>(
227             CalloutPriority::high, "service_docs", "U27-P2", false));
228 
229         EXPECT_EQ(lightPath.getLocationCodes(callouts),
230                   (std::vector<std::string>{}));
231     }
232 
233     // Normal FRU callout with a procedure callout not OK
234     {
235         CalloutVector callouts;
236         callouts.push_back(
237             std::make_unique<src::Callout>(CalloutPriority::high, "U27-P1",
238                                            "1234567", "aaaa", "123456789ABC"));
239         callouts.push_back(
240             std::make_unique<src::Callout>(CalloutPriority::high, "bmc_code"));
241 
242         EXPECT_EQ(lightPath.getLocationCodes(callouts),
243                   (std::vector<std::string>{}));
244     }
245 
246     // Trusted symbolic FRU callout with a non-trusted symbolic
247     // FRU callout not OK
248     {
249         CalloutVector callouts;
250         callouts.push_back(std::make_unique<src::Callout>(
251             CalloutPriority::high, "service_docs", "U27-P2", true));
252 
253         callouts.push_back(std::make_unique<src::Callout>(
254             CalloutPriority::high, "service_docs", "U27-P2", false));
255 
256         EXPECT_EQ(lightPath.getLocationCodes(callouts),
257                   (std::vector<std::string>{}));
258     }
259 }
260 
261 // Test the activate() function
262 TEST(ServiceIndicatorsTest, ActivateTest)
263 {
264     // pelFactory() will create a PEL with 1 callout with location code
265     // U42.  Test the LED for that gets activated.
266     {
267         MockDataInterface dataIface;
268         service_indicators::LightPath lightPath{dataIface};
269 
270         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
271             .WillOnce(Return("/system/chassis/processor"));
272 
273         EXPECT_CALL(dataIface,
274                     setFunctional("/system/chassis/processor", false))
275             .Times(1);
276 
277         EXPECT_CALL(dataIface,
278                     setCriticalAssociation("/system/chassis/processor"))
279             .Times(1);
280 
281         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
282         PEL pel{data};
283 
284         lightPath.activate(pel);
285     }
286 
287     // A non-info BMC PEL with no callouts will set the platform SAI LED.
288     {
289         MockDataInterface dataIface;
290         service_indicators::LightPath lightPath{dataIface};
291 
292         EXPECT_CALL(dataIface,
293                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
294                                    "platform_system_attention_indicator",
295                                    true))
296             .Times(1);
297 
298         auto data = pelDataFactory(TestPELType::pelSimple);
299         PEL pel{data};
300 
301         lightPath.activate(pel);
302     }
303 
304     // Make getInventoryFromLocCode fail - will set the platform SAI LED
305     {
306         MockDataInterface dataIface;
307         service_indicators::LightPath lightPath{dataIface};
308 
309         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
310             .WillOnce(Throw(std::runtime_error("Fail")));
311 
312         EXPECT_CALL(dataIface, setFunctional).Times(0);
313 
314         EXPECT_CALL(dataIface,
315                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
316                                    "platform_system_attention_indicator",
317                                    true))
318             .Times(1);
319 
320         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
321         PEL pel{data};
322 
323         lightPath.activate(pel);
324     }
325 
326     // Make setFunctional fail
327     {
328         MockDataInterface dataIface;
329         service_indicators::LightPath lightPath{dataIface};
330 
331         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
332             .WillOnce(Return("/system/chassis/processor"));
333 
334         EXPECT_CALL(dataIface,
335                     setFunctional("/system/chassis/processor", false))
336             .WillOnce(Throw(std::runtime_error("Fail")));
337 
338         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
339         PEL pel{data};
340 
341         lightPath.activate(pel);
342     }
343 
344     // Test setCriticalAssociation fail
345     {
346         MockDataInterface dataIface;
347         service_indicators::LightPath lightPath{dataIface};
348 
349         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
350             .WillOnce(Return("/system/chassis/processor"));
351 
352         EXPECT_CALL(dataIface,
353                     setCriticalAssociation("/system/chassis/processor"))
354             .WillOnce(Throw(std::runtime_error("Fail")));
355 
356         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
357         PEL pel{data};
358 
359         lightPath.activate(pel);
360     }
361 }
362