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(
272                 Return(std::vector<std::string>{"/system/chassis/processor"}));
273 
274         EXPECT_CALL(dataIface,
275                     setFunctional("/system/chassis/processor", false))
276             .Times(1);
277 
278         EXPECT_CALL(dataIface,
279                     setCriticalAssociation("/system/chassis/processor"))
280             .Times(1);
281 
282         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
283         PEL pel{data};
284 
285         lightPath.activate(pel);
286     }
287 
288     // With the same U42 callout, have it be associated with two
289     // inventory paths
290     {
291         MockDataInterface dataIface;
292         service_indicators::LightPath lightPath{dataIface};
293 
294         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
295             .WillOnce(Return(std::vector<std::string>{"/system/chassis/cpu0",
296                                                       "/system/chassis/cpu1"}));
297 
298         EXPECT_CALL(dataIface, setFunctional("/system/chassis/cpu0", false))
299             .Times(1);
300         EXPECT_CALL(dataIface, setFunctional("/system/chassis/cpu1", false))
301             .Times(1);
302 
303         EXPECT_CALL(dataIface, setCriticalAssociation("/system/chassis/cpu0"))
304             .Times(1);
305         EXPECT_CALL(dataIface, setCriticalAssociation("/system/chassis/cpu1"))
306             .Times(1);
307 
308         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
309         PEL pel{data};
310 
311         lightPath.activate(pel);
312     }
313 
314     // A non-info BMC PEL with no callouts will set the platform SAI LED.
315     {
316         MockDataInterface dataIface;
317         service_indicators::LightPath lightPath{dataIface};
318 
319         EXPECT_CALL(dataIface,
320                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
321                                    "platform_system_attention_indicator",
322                                    true))
323             .Times(1);
324 
325         auto data = pelDataFactory(TestPELType::pelSimple);
326         PEL pel{data};
327 
328         lightPath.activate(pel);
329     }
330 
331     // Make getInventoryFromLocCode fail - will set the platform SAI LED
332     {
333         MockDataInterface dataIface;
334         service_indicators::LightPath lightPath{dataIface};
335 
336         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
337             .WillOnce(Throw(std::runtime_error("Fail")));
338 
339         EXPECT_CALL(dataIface, setFunctional).Times(0);
340 
341         EXPECT_CALL(dataIface,
342                     assertLEDGroup("/xyz/openbmc_project/led/groups/"
343                                    "platform_system_attention_indicator",
344                                    true))
345             .Times(1);
346 
347         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
348         PEL pel{data};
349 
350         lightPath.activate(pel);
351     }
352 
353     // Make setFunctional fail
354     {
355         MockDataInterface dataIface;
356         service_indicators::LightPath lightPath{dataIface};
357 
358         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
359             .WillOnce(
360                 Return(std::vector<std::string>{"/system/chassis/processor"}));
361 
362         EXPECT_CALL(dataIface,
363                     setFunctional("/system/chassis/processor", false))
364             .WillOnce(Throw(std::runtime_error("Fail")));
365 
366         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
367         PEL pel{data};
368 
369         lightPath.activate(pel);
370     }
371 
372     // Test setCriticalAssociation fail
373     {
374         MockDataInterface dataIface;
375         service_indicators::LightPath lightPath{dataIface};
376 
377         EXPECT_CALL(dataIface, getInventoryFromLocCode("U42", 0, true))
378             .WillOnce(
379                 Return(std::vector<std::string>{"/system/chassis/processor"}));
380 
381         EXPECT_CALL(dataIface,
382                     setCriticalAssociation("/system/chassis/processor"))
383             .WillOnce(Throw(std::runtime_error("Fail")));
384 
385         auto data = pelFactory(1, 'O', 0x20, 0xA400, 500);
386         PEL pel{data};
387 
388         lightPath.activate(pel);
389     }
390 }
391