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, getFaultLEDGroup("/system/chassis/processor"))
274             .WillOnce(Return("/led/groups/cpu0"));
275 
276         EXPECT_CALL(dataIface, assertLEDGroup("/led/groups/cpu0", true))
277             .Times(1);
278 
279         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
280         PEL pel{data};
281 
282         lightPath.activate(pel);
283     }
284 
285     // A non-info BMC PEL with no callouts will set the platform SAI LED.
286     {
287         MockDataInterface dataIface;
288         service_indicators::LightPath lightPath{dataIface};
289 
290         EXPECT_CALL(dataIface,
291                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
292                                    "platform_system_attention_indicator",
293                                    true))
294             .Times(1);
295 
296         auto data = pelDataFactory(TestPELType::pelSimple);
297         PEL pel{data};
298 
299         lightPath.activate(pel);
300     }
301 
302     // Make getInventoryFromLocCode fail - will set the platform SAI LED
303     {
304         MockDataInterface dataIface;
305         service_indicators::LightPath lightPath{dataIface};
306 
307         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
308             .WillOnce(Throw(std::runtime_error("Fail")));
309 
310         EXPECT_CALL(dataIface, getFaultLEDGroup(_)).Times(0);
311 
312         EXPECT_CALL(dataIface,
313                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
314                                    "platform_system_attention_indicator",
315                                    true))
316             .Times(1);
317 
318         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
319         PEL pel{data};
320 
321         lightPath.activate(pel);
322     }
323 
324     // Make getFaultLEDGroup fail - will set the platform SAI LED
325     {
326         MockDataInterface dataIface;
327         service_indicators::LightPath lightPath{dataIface};
328 
329         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
330             .WillOnce(Return("/system/chassis/processor"));
331 
332         EXPECT_CALL(dataIface, getFaultLEDGroup("/system/chassis/processor"))
333             .WillOnce(Throw(std::runtime_error("Fail")));
334 
335         EXPECT_CALL(dataIface,
336                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
337                                    "platform_system_attention_indicator",
338                                    true))
339             .Times(1);
340 
341         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
342         PEL pel{data};
343 
344         lightPath.activate(pel);
345     }
346 
347     // Make assertLEDGroup fail
348     {
349         MockDataInterface dataIface;
350         service_indicators::LightPath lightPath{dataIface};
351 
352         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
353             .WillOnce(Return("/system/chassis/processor"));
354 
355         EXPECT_CALL(dataIface, getFaultLEDGroup("/system/chassis/processor"))
356             .WillOnce(Return("/led/groups/cpu0"));
357 
358         EXPECT_CALL(dataIface, assertLEDGroup("/led/groups/cpu0", true))
359             .WillOnce(Throw(std::runtime_error("Fail")));
360 
361         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
362         PEL pel{data};
363 
364         lightPath.activate(pel);
365     }
366 }
367