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
TEST(ServiceIndicatorsTest,IgnoreTest)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.
TEST(ServiceIndicatorsTest,OneCalloutPriorityTest)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.
TEST(ServiceIndicatorsTest,OneCalloutTypeTest)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).
TEST(ServiceIndicatorsTest,CalloutGroupingTest)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
TEST(ServiceIndicatorsTest,CalloutMixedTypesTest)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
TEST(ServiceIndicatorsTest,ActivateTest)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