1 /**
2 * Copyright © 2019 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/src.hpp"
17 #include "mocks.hpp"
18 #include "pel_utils.hpp"
19
20 #include <fstream>
21
22 #include <gtest/gtest.h>
23
24 using namespace openpower::pels;
25 using ::testing::_;
26 using ::testing::DoAll;
27 using ::testing::InvokeWithoutArgs;
28 using ::testing::NiceMock;
29 using ::testing::Return;
30 using ::testing::SetArgReferee;
31 using ::testing::Throw;
32 namespace fs = std::filesystem;
33
34 const auto testRegistry = R"(
35 {
36 "PELs":
37 [
38 {
39 "Name": "xyz.openbmc_project.Error.Test",
40 "Subsystem": "bmc_firmware",
41 "SRC":
42 {
43 "ReasonCode": "0xABCD",
44 "Words6To9":
45 {
46 "6":
47 {
48 "Description": "Component ID",
49 "AdditionalDataPropSource": "COMPID"
50 },
51 "7":
52 {
53 "Description": "Failure count",
54 "AdditionalDataPropSource": "FREQUENCY"
55 },
56 "8":
57 {
58 "Description": "Time period",
59 "AdditionalDataPropSource": "DURATION"
60 },
61 "9":
62 {
63 "Description": "Error code",
64 "AdditionalDataPropSource": "ERRORCODE"
65 }
66 }
67 },
68 "Documentation":
69 {
70 "Description": "A Component Fault",
71 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
72 "MessageArgSources":
73 [
74 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
75 ]
76 }
77 }
78 ]
79 }
80 )";
81
82 class SRCTest : public ::testing::Test
83 {
84 protected:
SetUpTestCase()85 static void SetUpTestCase()
86 {
87 char path[] = "/tmp/srctestXXXXXX";
88 regDir = mkdtemp(path);
89 }
90
TearDownTestCase()91 static void TearDownTestCase()
92 {
93 fs::remove_all(regDir);
94 }
95
writeData(const char * data)96 static std::string writeData(const char* data)
97 {
98 fs::path path = regDir / "registry.json";
99 std::ofstream stream{path};
100 stream << data;
101 return path;
102 }
103
104 static fs::path regDir;
105 };
106
107 fs::path SRCTest::regDir{};
108
TEST_F(SRCTest,UnflattenFlattenTestNoCallouts)109 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
110 {
111 auto data = pelDataFactory(TestPELType::primarySRCSection);
112
113 Stream stream{data};
114 SRC src{stream};
115
116 EXPECT_TRUE(src.valid());
117
118 EXPECT_EQ(src.header().id, 0x5053);
119 EXPECT_EQ(src.header().size, 0x50);
120 EXPECT_EQ(src.header().version, 0x01);
121 EXPECT_EQ(src.header().subType, 0x01);
122 EXPECT_EQ(src.header().componentID, 0x0202);
123
124 EXPECT_EQ(src.version(), 0x02);
125 EXPECT_EQ(src.flags(), 0x00);
126 EXPECT_EQ(src.hexWordCount(), 9);
127 EXPECT_EQ(src.size(), 0x48);
128
129 const auto& hexwords = src.hexwordData();
130 EXPECT_EQ(0x02020255, hexwords[0]);
131 EXPECT_EQ(0x03030310, hexwords[1]);
132 EXPECT_EQ(0x04040404, hexwords[2]);
133 EXPECT_EQ(0x05050505, hexwords[3]);
134 EXPECT_EQ(0x06060606, hexwords[4]);
135 EXPECT_EQ(0x07070707, hexwords[5]);
136 EXPECT_EQ(0x08080808, hexwords[6]);
137 EXPECT_EQ(0x09090909, hexwords[7]);
138
139 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
140 EXPECT_FALSE(src.callouts());
141
142 // Flatten
143 std::vector<uint8_t> newData;
144 Stream newStream{newData};
145
146 src.flatten(newStream);
147 EXPECT_EQ(data, newData);
148 }
149
TEST_F(SRCTest,UnflattenFlattenTest2Callouts)150 TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
151 {
152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
153
154 Stream stream{data};
155 SRC src{stream};
156
157 EXPECT_TRUE(src.valid());
158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
159
160 // Spot check the SRC fields, but they're the same as above
161 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
162
163 // There should be 2 callouts
164 const auto& calloutsSection = src.callouts();
165 ASSERT_TRUE(calloutsSection);
166 const auto& callouts = calloutsSection->callouts();
167 EXPECT_EQ(callouts.size(), 2);
168
169 // spot check that each callout has the right substructures
170 EXPECT_TRUE(callouts.front()->fruIdentity());
171 EXPECT_FALSE(callouts.front()->pceIdentity());
172 EXPECT_FALSE(callouts.front()->mru());
173
174 EXPECT_TRUE(callouts.back()->fruIdentity());
175 EXPECT_TRUE(callouts.back()->pceIdentity());
176 EXPECT_TRUE(callouts.back()->mru());
177
178 // Flatten
179 std::vector<uint8_t> newData;
180 Stream newStream{newData};
181
182 src.flatten(newStream);
183 EXPECT_EQ(data, newData);
184 }
185
186 // Create an SRC from the message registry
TEST_F(SRCTest,CreateTestNoCallouts)187 TEST_F(SRCTest, CreateTestNoCallouts)
188 {
189 message::Entry entry;
190 entry.src.type = 0xBD;
191 entry.src.reasonCode = 0xABCD;
192 entry.subsystem = 0x42;
193 entry.src.hexwordADFields = {
194 {5, {"TEST1", "DESCR1"}}, // Not a user defined word
195 {6, {"TEST1", "DESCR1"}},
196 {7, {"TEST2", "DESCR2"}},
197 {8, {"TEST3", "DESCR3"}},
198 {9, {"TEST4", "DESCR4"}}};
199
200 // Values for the SRC words pointed to above
201 std::map<std::string, std::string> adData{
202 {"TEST1", "0x12345678"},
203 {"TEST2", "12345678"},
204 {"TEST3", "0XDEF"},
205 {"TEST4", "Z"}};
206 AdditionalData ad{adData};
207 NiceMock<MockDataInterface> dataIface;
208
209 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
210
211 SRC src{entry, ad, dataIface};
212
213 EXPECT_TRUE(src.valid());
214 EXPECT_FALSE(src.isPowerFaultEvent());
215 EXPECT_EQ(src.size(), baseSRCSize);
216
217 const auto& hexwords = src.hexwordData();
218
219 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
220 // array index starts at 0 use the math in the [] below to make it easier
221 // to tell what is being accessed.
222 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
223 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
224 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
225 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
226 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
227
228 // Validate more fields here as the code starts filling them in.
229
230 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
231 // And that none of the error status flags are set
232 EXPECT_EQ(hexwords[5 - 2], 0);
233
234 // The user defined hex word fields specifed in the additional data.
235 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
236 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
237 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
238 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
239
240 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
241
242 // No callouts
243 EXPECT_FALSE(src.callouts());
244
245 // May as well spot check the flatten/unflatten
246 std::vector<uint8_t> data;
247 Stream stream{data};
248 src.flatten(stream);
249
250 stream.offset(0);
251 SRC newSRC{stream};
252
253 EXPECT_TRUE(newSRC.valid());
254 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
255 EXPECT_FALSE(newSRC.callouts());
256 }
257
258 // Test when the CCIN string isn't a 4 character number
TEST_F(SRCTest,BadCCINTest)259 TEST_F(SRCTest, BadCCINTest)
260 {
261 message::Entry entry;
262 entry.src.type = 0xBD;
263 entry.src.reasonCode = 0xABCD;
264 entry.subsystem = 0x42;
265
266 std::map<std::string, std::string> adData{};
267 AdditionalData ad{adData};
268 NiceMock<MockDataInterface> dataIface;
269
270 // First it isn't a number, then it is too long,
271 // then it is empty.
272 EXPECT_CALL(dataIface, getMotherboardCCIN)
273 .WillOnce(Return("X"))
274 .WillOnce(Return("12345"))
275 .WillOnce(Return(""));
276
277 // The CCIN in the first half should still be 0 each time.
278 {
279 SRC src{entry, ad, dataIface};
280 EXPECT_TRUE(src.valid());
281 const auto& hexwords = src.hexwordData();
282 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
283 }
284
285 {
286 SRC src{entry, ad, dataIface};
287 EXPECT_TRUE(src.valid());
288 const auto& hexwords = src.hexwordData();
289 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
290 }
291
292 {
293 SRC src{entry, ad, dataIface};
294 EXPECT_TRUE(src.valid());
295 const auto& hexwords = src.hexwordData();
296 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
297 }
298 }
299
300 // Test the getErrorDetails function
TEST_F(SRCTest,MessageSubstitutionTest)301 TEST_F(SRCTest, MessageSubstitutionTest)
302 {
303 auto path = SRCTest::writeData(testRegistry);
304 message::Registry registry{path};
305 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
306
307 std::map<std::string, std::string> adData{
308 {"COMPID", "0x1"},
309 {"FREQUENCY", "0x4"},
310 {"DURATION", "30"},
311 {"ERRORCODE", "0x01ABCDEF"}};
312 AdditionalData ad{adData};
313 NiceMock<MockDataInterface> dataIface;
314
315 SRC src{*entry, ad, dataIface};
316 EXPECT_TRUE(src.valid());
317
318 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
319 ASSERT_TRUE(errorDetails);
320 EXPECT_EQ(errorDetails.value(),
321 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
322 "with ErrorCode 0x01ABCDEF");
323 }
324 // Test that an inventory path callout string is
325 // converted into the appropriate FRU callout.
TEST_F(SRCTest,InventoryCalloutTest)326 TEST_F(SRCTest, InventoryCalloutTest)
327 {
328 message::Entry entry;
329 entry.src.type = 0xBD;
330 entry.src.reasonCode = 0xABCD;
331 entry.subsystem = 0x42;
332
333 std::map<std::string, std::string> adData{
334 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
335 AdditionalData ad{adData};
336 NiceMock<MockDataInterface> dataIface;
337
338 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
339 .WillOnce(Return("UTMS-P1"));
340
341 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
342 .Times(1)
343 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
344 SetArgReferee<3>("123456789ABC")));
345
346 SRC src{entry, ad, dataIface};
347 EXPECT_TRUE(src.valid());
348
349 ASSERT_TRUE(src.callouts());
350
351 EXPECT_EQ(src.callouts()->callouts().size(), 1);
352
353 auto& callout = src.callouts()->callouts().front();
354
355 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
356 EXPECT_EQ(callout->priority(), 'H');
357
358 auto& fru = callout->fruIdentity();
359
360 EXPECT_EQ(fru->getPN().value(), "1234567");
361 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
362 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
363
364 // flatten and unflatten
365 std::vector<uint8_t> data;
366 Stream stream{data};
367 src.flatten(stream);
368
369 stream.offset(0);
370 SRC newSRC{stream};
371 EXPECT_TRUE(newSRC.valid());
372 ASSERT_TRUE(src.callouts());
373 EXPECT_EQ(src.callouts()->callouts().size(), 1);
374 }
375
376 // Test that when the location code can't be obtained that
377 // no callout is added.
TEST_F(SRCTest,InventoryCalloutNoLocCodeTest)378 TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
379 {
380 message::Entry entry;
381 entry.src.type = 0xBD;
382 entry.src.reasonCode = 0xABCD;
383 entry.subsystem = 0x42;
384
385 std::map<std::string, std::string> adData{
386 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
387 AdditionalData ad{adData};
388 NiceMock<MockDataInterface> dataIface;
389
390 auto func = []() {
391 throw sdbusplus::exception::SdBusError(5, "Error");
392 return std::string{};
393 };
394
395 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
396 .Times(1)
397 .WillOnce(InvokeWithoutArgs(func));
398
399 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
400
401 SRC src{entry, ad, dataIface};
402 EXPECT_TRUE(src.valid());
403
404 ASSERT_FALSE(src.callouts());
405
406 // flatten and unflatten
407 std::vector<uint8_t> data;
408 Stream stream{data};
409 src.flatten(stream);
410
411 stream.offset(0);
412 SRC newSRC{stream};
413 EXPECT_TRUE(newSRC.valid());
414 ASSERT_FALSE(src.callouts());
415 }
416
417 // Test that when the VPD can't be obtained that
418 // a callout is still created.
TEST_F(SRCTest,InventoryCalloutNoVPDTest)419 TEST_F(SRCTest, InventoryCalloutNoVPDTest)
420 {
421 message::Entry entry;
422 entry.src.type = 0xBD;
423 entry.src.reasonCode = 0xABCD;
424 entry.subsystem = 0x42;
425
426 std::map<std::string, std::string> adData{
427 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
428 AdditionalData ad{adData};
429 NiceMock<MockDataInterface> dataIface;
430
431 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
432 .Times(1)
433 .WillOnce(Return("UTMS-P10"));
434
435 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
436
437 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
438 .Times(1)
439 .WillOnce(InvokeWithoutArgs(func));
440
441 SRC src{entry, ad, dataIface};
442 EXPECT_TRUE(src.valid());
443 ASSERT_TRUE(src.callouts());
444 EXPECT_EQ(src.callouts()->callouts().size(), 1);
445
446 auto& callout = src.callouts()->callouts().front();
447 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
448 EXPECT_EQ(callout->priority(), 'H');
449
450 auto& fru = callout->fruIdentity();
451
452 EXPECT_EQ(fru->getPN(), "");
453 EXPECT_EQ(fru->getCCIN(), "");
454 EXPECT_EQ(fru->getSN(), "");
455 EXPECT_FALSE(fru->getMaintProc());
456
457 // flatten and unflatten
458 std::vector<uint8_t> data;
459 Stream stream{data};
460 src.flatten(stream);
461
462 stream.offset(0);
463 SRC newSRC{stream};
464 EXPECT_TRUE(newSRC.valid());
465 ASSERT_TRUE(src.callouts());
466 EXPECT_EQ(src.callouts()->callouts().size(), 1);
467 }
468
TEST_F(SRCTest,RegistryCalloutTest)469 TEST_F(SRCTest, RegistryCalloutTest)
470 {
471 message::Entry entry;
472 entry.src.type = 0xBD;
473 entry.src.reasonCode = 0xABCD;
474 entry.src.deconfigFlag = true;
475 entry.src.checkstopFlag = true;
476 entry.subsystem = 0x42;
477
478 entry.callouts = R"(
479 [
480 {
481 "System": "systemA",
482 "CalloutList":
483 [
484 {
485 "Priority": "high",
486 "SymbolicFRU": "service_docs"
487 },
488 {
489 "Priority": "medium",
490 "Procedure": "BMC0001"
491 }
492 ]
493 },
494 {
495 "System": "systemB",
496 "CalloutList":
497 [
498 {
499 "Priority": "high",
500 "LocCode": "P0-C8",
501 "SymbolicFRUTrusted": "service_docs"
502 },
503 {
504 "Priority": "medium",
505 "SymbolicFRUTrusted": "service_docs"
506 }
507 ]
508 },
509 {
510 "System": "systemC",
511 "CalloutList":
512 [
513 {
514 "Priority": "high",
515 "LocCode": "P0-C8"
516 },
517 {
518 "Priority": "medium",
519 "LocCode": "P0-C9"
520 }
521 ]
522 }
523 ])"_json;
524
525 {
526 // Call out a symbolic FRU and a procedure
527 AdditionalData ad;
528 NiceMock<MockDataInterface> dataIface;
529 std::vector<std::string> names{"systemA"};
530
531 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
532
533 SRC src{entry, ad, dataIface};
534
535 EXPECT_TRUE(
536 src.getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured));
537 EXPECT_TRUE(src.getErrorStatusFlag(SRC::ErrorStatusFlags::hwCheckstop));
538
539 const auto& hexwords = src.hexwordData();
540 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured) |
541 static_cast<uint32_t>(SRC::ErrorStatusFlags::hwCheckstop);
542 EXPECT_EQ(hexwords[5 - 2] & mask, mask);
543
544 auto& callouts = src.callouts()->callouts();
545 ASSERT_EQ(callouts.size(), 2);
546
547 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
548 EXPECT_EQ(callouts[0]->priority(), 'H');
549
550 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
551 EXPECT_EQ(callouts[1]->priority(), 'M');
552
553 auto& fru1 = callouts[0]->fruIdentity();
554 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
555 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
556 EXPECT_FALSE(fru1->getMaintProc());
557 EXPECT_FALSE(fru1->getSN());
558 EXPECT_FALSE(fru1->getCCIN());
559
560 auto& fru2 = callouts[1]->fruIdentity();
561 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
562 EXPECT_EQ(fru2->failingComponentType(),
563 src::FRUIdentity::maintenanceProc);
564 EXPECT_FALSE(fru2->getPN());
565 EXPECT_FALSE(fru2->getSN());
566 EXPECT_FALSE(fru2->getCCIN());
567 }
568
569 {
570 // Call out a trusted symbolic FRU with a location code, and
571 // another one without.
572 AdditionalData ad;
573 NiceMock<MockDataInterface> dataIface;
574 std::vector<std::string> names{"systemB"};
575
576 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
577 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
578
579 SRC src{entry, ad, dataIface};
580
581 auto& callouts = src.callouts()->callouts();
582 EXPECT_EQ(callouts.size(), 2);
583
584 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
585 EXPECT_EQ(callouts[0]->priority(), 'H');
586
587 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
588 EXPECT_EQ(callouts[1]->priority(), 'M');
589
590 auto& fru1 = callouts[0]->fruIdentity();
591 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
592 EXPECT_EQ(fru1->failingComponentType(),
593 src::FRUIdentity::symbolicFRUTrustedLocCode);
594 EXPECT_FALSE(fru1->getMaintProc());
595 EXPECT_FALSE(fru1->getSN());
596 EXPECT_FALSE(fru1->getCCIN());
597
598 // It asked for a trusted symbolic FRU, but no location code
599 // was provided so it is switched back to a normal one
600 auto& fru2 = callouts[1]->fruIdentity();
601 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
602 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
603 EXPECT_FALSE(fru2->getMaintProc());
604 EXPECT_FALSE(fru2->getSN());
605 EXPECT_FALSE(fru2->getCCIN());
606 }
607
608 {
609 // Two hardware callouts
610 AdditionalData ad;
611 NiceMock<MockDataInterface> dataIface;
612 std::vector<std::string> names{"systemC"};
613
614 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
615
616 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
617 .WillOnce(Return("UXXX-P0-C8"));
618
619 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
620 .WillOnce(Return("UXXX-P0-C9"));
621
622 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
623 .WillOnce(Return(std::vector<std::string>{
624 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
625
626 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
627 .WillOnce(Return(std::vector<std::string>{
628 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"}));
629
630 EXPECT_CALL(
631 dataIface,
632 getHWCalloutFields(
633 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
634 _))
635 .Times(1)
636 .WillOnce(
637 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
638 SetArgReferee<3>("123456789ABC")));
639
640 EXPECT_CALL(
641 dataIface,
642 getHWCalloutFields(
643 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
644 _))
645 .Times(1)
646 .WillOnce(
647 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
648 SetArgReferee<3>("23456789ABCD")));
649
650 SRC src{entry, ad, dataIface};
651
652 auto& callouts = src.callouts()->callouts();
653 EXPECT_EQ(callouts.size(), 2);
654
655 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
656 EXPECT_EQ(callouts[0]->priority(), 'H');
657
658 auto& fru1 = callouts[0]->fruIdentity();
659 EXPECT_EQ(fru1->getPN().value(), "1234567");
660 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
661 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
662
663 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
664 EXPECT_EQ(callouts[1]->priority(), 'M');
665
666 auto& fru2 = callouts[1]->fruIdentity();
667 EXPECT_EQ(fru2->getPN().value(), "2345678");
668 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
669 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
670 }
671 }
672
673 // Test that a symbolic FRU with a trusted location code callout
674 // from the registry can get its location from the
675 // CALLOUT_INVENTORY_PATH AdditionalData entry.
TEST_F(SRCTest,SymbolicFRUWithInvPathTest)676 TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
677 {
678 message::Entry entry;
679 entry.src.type = 0xBD;
680 entry.src.reasonCode = 0xABCD;
681 entry.subsystem = 0x42;
682
683 entry.callouts = R"(
684 [{
685 "CalloutList":
686 [
687 {
688 "Priority": "high",
689 "SymbolicFRUTrusted": "service_docs",
690 "UseInventoryLocCode": true
691 },
692 {
693 "Priority": "medium",
694 "LocCode": "P0-C8",
695 "SymbolicFRUTrusted": "pwrsply"
696 }
697 ]
698 }])"_json;
699
700 {
701 // The location code for the first symbolic FRU callout will
702 // come from this inventory path since UseInventoryLocCode is set.
703 // In this case there will be no normal FRU callout for the motherboard.
704 std::map<std::string, std::string> adData{
705 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
706 AdditionalData ad{adData};
707 NiceMock<MockDataInterface> dataIface;
708 std::vector<std::string> names{"systemA"};
709
710 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
711
712 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
713 .Times(1)
714 .WillOnce(Return("Ufcs-P10"));
715
716 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
717 .WillOnce(Return("Ufcs-P0-C8"));
718
719 SRC src{entry, ad, dataIface};
720
721 auto& callouts = src.callouts()->callouts();
722 EXPECT_EQ(callouts.size(), 2);
723
724 // The location code for the first symbolic FRU callout with a
725 // trusted location code comes from the motherboard.
726 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
727 EXPECT_EQ(callouts[0]->priority(), 'H');
728 auto& fru1 = callouts[0]->fruIdentity();
729 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
730 EXPECT_EQ(fru1->failingComponentType(),
731 src::FRUIdentity::symbolicFRUTrustedLocCode);
732
733 // The second trusted symbolic FRU callouts uses the location
734 // code in the registry as usual.
735 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
736 EXPECT_EQ(callouts[1]->priority(), 'M');
737 auto& fru2 = callouts[1]->fruIdentity();
738 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
739 EXPECT_EQ(fru2->failingComponentType(),
740 src::FRUIdentity::symbolicFRUTrustedLocCode);
741 }
742
743 {
744 // This time say we want to use the location code from
745 // the inventory, but don't pass it in and the callout should
746 // end up a regular symbolic FRU
747 entry.callouts = R"(
748 [{
749 "CalloutList":
750 [
751 {
752 "Priority": "high",
753 "SymbolicFRUTrusted": "service_docs",
754 "UseInventoryLocCode": true
755 }
756 ]
757 }])"_json;
758
759 AdditionalData ad;
760 NiceMock<MockDataInterface> dataIface;
761 std::vector<std::string> names{"systemA"};
762
763 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
764
765 SRC src{entry, ad, dataIface};
766
767 auto& callouts = src.callouts()->callouts();
768 EXPECT_EQ(callouts.size(), 1);
769
770 EXPECT_EQ(callouts[0]->locationCode(), "");
771 EXPECT_EQ(callouts[0]->priority(), 'H');
772 auto& fru1 = callouts[0]->fruIdentity();
773 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
774 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
775 }
776 }
777
778 // Test looking up device path fails in the callout jSON.
TEST_F(SRCTest,DevicePathCalloutTest)779 TEST_F(SRCTest, DevicePathCalloutTest)
780 {
781 message::Entry entry;
782 entry.src.type = 0xBD;
783 entry.src.reasonCode = 0xABCD;
784 entry.subsystem = 0x42;
785
786 const auto calloutJSON = R"(
787 {
788 "I2C":
789 {
790 "14":
791 {
792 "114":
793 {
794 "Callouts":[
795 {
796 "Name": "/chassis/motherboard/cpu0",
797 "LocationCode": "P1-C40",
798 "Priority": "H"
799 },
800 {
801 "Name": "/chassis/motherboard",
802 "LocationCode": "P1",
803 "Priority": "M"
804 },
805 {
806 "Name": "/chassis/motherboard/bmc",
807 "LocationCode": "P1-C15",
808 "Priority": "L"
809 }
810 ],
811 "Dest": "proc 0 target"
812 }
813 }
814 }
815 })";
816
817 auto dataPath = getPELReadOnlyDataPath();
818 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
819 file << calloutJSON;
820 file.close();
821
822 NiceMock<MockDataInterface> dataIface;
823 std::vector<std::string> names{"systemA"};
824
825 EXPECT_CALL(dataIface, getSystemNames)
826 .Times(5)
827 .WillRepeatedly(Return(names));
828
829 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
830 .Times(3)
831 .WillRepeatedly(Return(std::vector<std::string>{
832 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
833
834 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
835 .Times(3)
836 .WillRepeatedly(Return(std::vector<std::string>{
837 "/xyz/openbmc_project/inventory/chassis/motherboard"}));
838
839 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
840 .Times(3)
841 .WillRepeatedly(Return(std::vector<std::string>{
842 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"}));
843
844 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
845 .Times(3)
846 .WillRepeatedly(Return("Ufcs-P1-C40"));
847
848 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
849 .Times(3)
850 .WillRepeatedly(Return("Ufcs-P1"));
851
852 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
853 .Times(3)
854 .WillRepeatedly(Return("Ufcs-P1-C15"));
855
856 EXPECT_CALL(dataIface,
857 getHWCalloutFields(
858 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0",
859 _, _, _))
860 .Times(3)
861 .WillRepeatedly(
862 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
863 SetArgReferee<3>("123456789ABC")));
864 EXPECT_CALL(
865 dataIface,
866 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
867 _, _, _))
868 .Times(3)
869 .WillRepeatedly(
870 DoAll(SetArgReferee<1>("7654321"), SetArgReferee<2>("MMMM"),
871 SetArgReferee<3>("CBA987654321")));
872 EXPECT_CALL(dataIface,
873 getHWCalloutFields(
874 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _,
875 _, _))
876 .Times(3)
877 .WillRepeatedly(
878 DoAll(SetArgReferee<1>("7123456"), SetArgReferee<2>("BBBB"),
879 SetArgReferee<3>("C123456789AB")));
880
881 // Call this below with different AdditionalData values that
882 // result in the same callouts.
883 auto checkCallouts = [&entry, &dataIface](const auto& items) {
884 AdditionalData ad{items};
885 SRC src{entry, ad, dataIface};
886
887 ASSERT_TRUE(src.callouts());
888 auto& callouts = src.callouts()->callouts();
889
890 ASSERT_EQ(callouts.size(), 3);
891
892 {
893 EXPECT_EQ(callouts[0]->priority(), 'H');
894 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
895
896 auto& fru = callouts[0]->fruIdentity();
897 EXPECT_EQ(fru->getPN().value(), "1234567");
898 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
899 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
900 }
901 {
902 EXPECT_EQ(callouts[1]->priority(), 'M');
903 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
904
905 auto& fru = callouts[1]->fruIdentity();
906 EXPECT_EQ(fru->getPN().value(), "7654321");
907 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
908 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
909 }
910 {
911 EXPECT_EQ(callouts[2]->priority(), 'L');
912 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
913
914 auto& fru = callouts[2]->fruIdentity();
915 EXPECT_EQ(fru->getPN().value(), "7123456");
916 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
917 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
918 }
919 };
920
921 {
922 // Callouts based on the device path
923 std::map<std::string, std::string> items{
924 {"CALLOUT_ERRNO", "5"},
925 {"CALLOUT_DEVICE_PATH",
926 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}};
927
928 checkCallouts(items);
929 }
930
931 {
932 // Callouts based on the I2C bus and address
933 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
934 {"CALLOUT_IIC_BUS", "14"},
935 {"CALLOUT_IIC_ADDR", "0x72"}};
936 checkCallouts(items);
937 }
938
939 {
940 // Also based on I2C bus and address, but with bus = /dev/i2c-14
941 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
942 {"CALLOUT_IIC_BUS", "14"},
943 {"CALLOUT_IIC_ADDR", "0x72"}};
944 checkCallouts(items);
945 }
946
947 {
948 // Callout not found
949 std::map<std::string, std::string> items{
950 {"CALLOUT_ERRNO", "5"},
951 {"CALLOUT_DEVICE_PATH",
952 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}};
953
954 AdditionalData ad{items};
955 SRC src{entry, ad, dataIface};
956
957 EXPECT_FALSE(src.callouts());
958 ASSERT_EQ(src.getDebugData().size(), 1);
959 EXPECT_EQ(src.getDebugData()[0],
960 "Problem looking up I2C callouts on 24 18: "
961 "[json.exception.out_of_range.403] key '24' not found");
962 }
963
964 {
965 // Callout not found
966 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
967 {"CALLOUT_IIC_BUS", "22"},
968 {"CALLOUT_IIC_ADDR", "0x99"}};
969 AdditionalData ad{items};
970 SRC src{entry, ad, dataIface};
971
972 EXPECT_FALSE(src.callouts());
973 ASSERT_EQ(src.getDebugData().size(), 1);
974 EXPECT_EQ(src.getDebugData()[0],
975 "Problem looking up I2C callouts on 22 153: "
976 "[json.exception.out_of_range.403] key '22' not found");
977 }
978
979 fs::remove_all(dataPath);
980 }
981
982 // Test when callouts are passed in via JSON
TEST_F(SRCTest,JsonCalloutsTest)983 TEST_F(SRCTest, JsonCalloutsTest)
984 {
985 const auto jsonCallouts = R"(
986 [
987 {
988 "LocationCode": "P0-C1",
989 "Priority": "H",
990 "MRUs": [
991 {
992 "ID": 42,
993 "Priority": "H"
994 },
995 {
996 "ID": 43,
997 "Priority": "M"
998 }
999 ]
1000 },
1001 {
1002 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1003 "Priority": "M",
1004 "Guarded": true,
1005 "Deconfigured": true
1006 },
1007 {
1008 "Procedure": "PROCEDU",
1009 "Priority": "A"
1010 },
1011 {
1012 "SymbolicFRU": "TRUSTED",
1013 "Priority": "B",
1014 "TrustedLocationCode": true,
1015 "LocationCode": "P1-C23"
1016 },
1017 {
1018 "SymbolicFRU": "FRUTST1",
1019 "Priority": "C",
1020 "LocationCode": "P1-C24"
1021 },
1022 {
1023 "SymbolicFRU": "FRUTST2LONG",
1024 "Priority": "L"
1025 },
1026 {
1027 "Procedure": "fsi_path",
1028 "Priority": "L"
1029 },
1030 {
1031 "SymbolicFRU": "ambient_temp",
1032 "Priority": "L"
1033 }
1034 ]
1035 )"_json;
1036
1037 message::Entry entry;
1038 entry.src.type = 0xBD;
1039 entry.src.reasonCode = 0xABCD;
1040 entry.subsystem = 0x42;
1041
1042 AdditionalData ad;
1043 NiceMock<MockDataInterface> dataIface;
1044
1045 // Callout 0 mock calls
1046 {
1047 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1048 .Times(1)
1049 .WillOnce(Return("UXXX-P0-C1"));
1050 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1051 .Times(1)
1052 .WillOnce(Return(std::vector<std::string>{
1053 "/inv/system/chassis/motherboard/bmc"}));
1054 EXPECT_CALL(
1055 dataIface,
1056 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1057 .Times(1)
1058 .WillOnce(
1059 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1060 SetArgReferee<3>("123456789ABC")));
1061 }
1062 // Callout 1 mock calls
1063 {
1064 EXPECT_CALL(dataIface,
1065 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1066 .WillOnce(Return("UYYY-P5"));
1067 EXPECT_CALL(
1068 dataIface,
1069 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1070 .Times(1)
1071 .WillOnce(
1072 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
1073 SetArgReferee<3>("23456789ABCD")));
1074 }
1075 // Callout 3 mock calls
1076 {
1077 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1078 .Times(1)
1079 .WillOnce(Return("UXXX-P1-C23"));
1080 }
1081 // Callout 4 mock calls
1082 {
1083 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1084 .Times(1)
1085 .WillOnce(Return("UXXX-P1-C24"));
1086 }
1087
1088 SRC src{entry, ad, jsonCallouts, dataIface};
1089 ASSERT_TRUE(src.callouts());
1090
1091 // Check the guarded and deconfigured flags
1092 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1093
1094 const auto& callouts = src.callouts()->callouts();
1095 ASSERT_EQ(callouts.size(), 8);
1096
1097 // Check callout 0
1098 {
1099 EXPECT_EQ(callouts[0]->priority(), 'H');
1100 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1101
1102 auto& fru = callouts[0]->fruIdentity();
1103 EXPECT_EQ(fru->getPN().value(), "1234567");
1104 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1105 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1106 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1107
1108 auto& mruCallouts = callouts[0]->mru();
1109 ASSERT_TRUE(mruCallouts);
1110 auto& mrus = mruCallouts->mrus();
1111 ASSERT_EQ(mrus.size(), 2);
1112 EXPECT_EQ(mrus[0].id, 42);
1113 EXPECT_EQ(mrus[0].priority, 'H');
1114 EXPECT_EQ(mrus[1].id, 43);
1115 EXPECT_EQ(mrus[1].priority, 'M');
1116 }
1117
1118 // Check callout 1
1119 {
1120 EXPECT_EQ(callouts[1]->priority(), 'M');
1121 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1122
1123 auto& fru = callouts[1]->fruIdentity();
1124 EXPECT_EQ(fru->getPN().value(), "2345678");
1125 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1126 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1127 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1128 }
1129
1130 // Check callout 2
1131 {
1132 EXPECT_EQ(callouts[2]->priority(), 'A');
1133 EXPECT_EQ(callouts[2]->locationCode(), "");
1134
1135 auto& fru = callouts[2]->fruIdentity();
1136 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1137 EXPECT_EQ(fru->failingComponentType(),
1138 src::FRUIdentity::maintenanceProc);
1139 }
1140
1141 // Check callout 3
1142 {
1143 EXPECT_EQ(callouts[3]->priority(), 'B');
1144 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1145
1146 auto& fru = callouts[3]->fruIdentity();
1147 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1148 EXPECT_EQ(fru->failingComponentType(),
1149 src::FRUIdentity::symbolicFRUTrustedLocCode);
1150 }
1151
1152 // Check callout 4
1153 {
1154 EXPECT_EQ(callouts[4]->priority(), 'C');
1155 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1156
1157 auto& fru = callouts[4]->fruIdentity();
1158 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1159 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1160 }
1161
1162 // Check callout 5
1163 {
1164 EXPECT_EQ(callouts[5]->priority(), 'L');
1165 EXPECT_EQ(callouts[5]->locationCode(), "");
1166
1167 auto& fru = callouts[5]->fruIdentity();
1168 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1169 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1170 }
1171
1172 // Check callout 6
1173 {
1174 EXPECT_EQ(callouts[6]->priority(), 'L');
1175 EXPECT_EQ(callouts[6]->locationCode(), "");
1176
1177 auto& fru = callouts[6]->fruIdentity();
1178 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004");
1179 EXPECT_EQ(fru->failingComponentType(),
1180 src::FRUIdentity::maintenanceProc);
1181 }
1182
1183 // Check callout 7
1184 {
1185 EXPECT_EQ(callouts[7]->priority(), 'L');
1186 EXPECT_EQ(callouts[7]->locationCode(), "");
1187
1188 auto& fru = callouts[7]->fruIdentity();
1189 EXPECT_EQ(fru->getPN().value(), "AMBTEMP");
1190 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1191 }
1192
1193 // Check that it didn't find any errors
1194 const auto& data = src.getDebugData();
1195 EXPECT_TRUE(data.empty());
1196 }
1197
TEST_F(SRCTest,JsonBadCalloutsTest)1198 TEST_F(SRCTest, JsonBadCalloutsTest)
1199 {
1200 // The first call will have a Throw in a mock call.
1201 // The second will have a different Throw in a mock call.
1202 // The others have issues with the Priority field.
1203 const auto jsonCallouts = R"(
1204 [
1205 {
1206 "LocationCode": "P0-C1",
1207 "Priority": "H"
1208 },
1209 {
1210 "LocationCode": "P0-C2",
1211 "Priority": "H"
1212 },
1213 {
1214 "LocationCode": "P0-C3"
1215 },
1216 {
1217 "LocationCode": "P0-C4",
1218 "Priority": "X"
1219 }
1220 ]
1221 )"_json;
1222
1223 message::Entry entry;
1224 entry.src.type = 0xBD;
1225 entry.src.reasonCode = 0xABCD;
1226 entry.subsystem = 0x42;
1227
1228 AdditionalData ad;
1229 NiceMock<MockDataInterface> dataIface;
1230
1231 // Callout 0 mock calls
1232 // Expand location code will fail, so the unexpanded location
1233 // code should show up in the callout instead.
1234 {
1235 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1236 .WillOnce(Throw(std::runtime_error("Fail")));
1237
1238 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1239 .Times(1)
1240 .WillOnce(Return(std::vector<std::string>{
1241 "/inv/system/chassis/motherboard/bmc"}));
1242 EXPECT_CALL(
1243 dataIface,
1244 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1245 .Times(1)
1246 .WillOnce(
1247 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1248 SetArgReferee<3>("123456789ABC")));
1249 }
1250
1251 // Callout 1 mock calls
1252 // getInventoryFromLocCode will fail
1253 {
1254 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1255 .Times(1)
1256 .WillOnce(Return("UXXX-P0-C2"));
1257
1258 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1259 .Times(1)
1260 .WillOnce(Throw(std::runtime_error("Fail")));
1261 }
1262
1263 SRC src{entry, ad, jsonCallouts, dataIface};
1264
1265 ASSERT_TRUE(src.callouts());
1266
1267 const auto& callouts = src.callouts()->callouts();
1268
1269 // Only the first callout was successful
1270 ASSERT_EQ(callouts.size(), 1);
1271
1272 {
1273 EXPECT_EQ(callouts[0]->priority(), 'H');
1274 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1275
1276 auto& fru = callouts[0]->fruIdentity();
1277 EXPECT_EQ(fru->getPN().value(), "1234567");
1278 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1279 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1280 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1281 }
1282
1283 const auto& data = src.getDebugData();
1284 ASSERT_EQ(data.size(), 4);
1285 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1286 EXPECT_STREQ(data[1].c_str(),
1287 "Failed extracting callout data from JSON: Unable to "
1288 "get inventory path from location code: P0-C2: Fail");
1289 EXPECT_STREQ(data[2].c_str(),
1290 "Failed extracting callout data from JSON: "
1291 "[json.exception.out_of_range.403] key 'Priority' not found");
1292 EXPECT_STREQ(data[3].c_str(),
1293 "Failed extracting callout data from JSON: Invalid "
1294 "priority 'X' found in JSON callout");
1295 }
1296
1297 // Test that an inventory path callout can have
1298 // a different priority than H.
TEST_F(SRCTest,InventoryCalloutTestPriority)1299 TEST_F(SRCTest, InventoryCalloutTestPriority)
1300 {
1301 message::Entry entry;
1302 entry.src.type = 0xBD;
1303 entry.src.reasonCode = 0xABCD;
1304 entry.subsystem = 0x42;
1305
1306 std::map<std::string, std::string> adData{
1307 {"CALLOUT_INVENTORY_PATH", "motherboard"}, {"CALLOUT_PRIORITY", "M"}};
1308 AdditionalData ad{adData};
1309 NiceMock<MockDataInterface> dataIface;
1310
1311 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1312 .WillOnce(Return("UTMS-P1"));
1313
1314 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1315 .Times(1)
1316 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1317 SetArgReferee<3>("123456789ABC")));
1318
1319 SRC src{entry, ad, dataIface};
1320 EXPECT_TRUE(src.valid());
1321
1322 ASSERT_TRUE(src.callouts());
1323
1324 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1325
1326 auto& callout = src.callouts()->callouts().front();
1327
1328 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1329 EXPECT_EQ(callout->priority(), 'M');
1330 }
1331
1332 // Test SRC with additional data - PEL_SUBSYSTEM
TEST_F(SRCTest,TestPELSubsystem)1333 TEST_F(SRCTest, TestPELSubsystem)
1334 {
1335 message::Entry entry;
1336 entry.src.type = 0xBD;
1337 entry.src.reasonCode = 0xABCD;
1338 entry.subsystem = 0x42;
1339
1340 // Values for the SRC words pointed to above
1341 std::map<std::string, std::string> adData{{"PEL_SUBSYSTEM", "0x20"}};
1342 AdditionalData ad{adData};
1343 NiceMock<MockDataInterface> dataIface;
1344
1345 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1346
1347 SRC src{entry, ad, dataIface};
1348
1349 EXPECT_TRUE(src.valid());
1350
1351 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1352 }
1353
setAsciiString(std::vector<uint8_t> & src,const std::string & value)1354 void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1355 {
1356 assert(40 + value.size() <= src.size());
1357
1358 for (size_t i = 0; i < value.size(); i++)
1359 {
1360 src[40 + i] = value[i];
1361 }
1362 }
1363
TEST_F(SRCTest,TestGetProgressCode)1364 TEST_F(SRCTest, TestGetProgressCode)
1365 {
1366 {
1367 // A real SRC with CC009184
1368 std::vector<uint8_t> src{
1369 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1370 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1371 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1372 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1373 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1374
1375 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1376 }
1377
1378 {
1379 // A real SRC with STANDBY
1380 std::vector<uint8_t> src{
1381 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1382 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1384 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1385 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1386
1387 EXPECT_EQ(SRC::getProgressCode(src), 0);
1388 }
1389
1390 {
1391 // A real SRC with CC009184, but 1 byte too short
1392 std::vector<uint8_t> src{
1393 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1394 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1395 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1396 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1397 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1398 src.resize(71);
1399 EXPECT_EQ(SRC::getProgressCode(src), 0);
1400 }
1401
1402 {
1403 // A few different ones
1404 const std::map<std::string, uint32_t> progressCodes{
1405 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1406 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1407 {"1234567X", 0}, {"1 ", 0}};
1408
1409 std::vector<uint8_t> src(72, 0x0);
1410
1411 for (const auto& [code, expected] : progressCodes)
1412 {
1413 setAsciiString(src, code);
1414 EXPECT_EQ(SRC::getProgressCode(src), expected);
1415 }
1416
1417 // empty
1418 src.clear();
1419 EXPECT_EQ(SRC::getProgressCode(src), 0);
1420 }
1421 }
1422
1423 // Test progress is in right SRC hex data field
TEST_F(SRCTest,TestProgressCodeField)1424 TEST_F(SRCTest, TestProgressCodeField)
1425 {
1426 message::Entry entry;
1427 entry.src.type = 0xBD;
1428 entry.src.reasonCode = 0xABCD;
1429 entry.subsystem = 0x42;
1430
1431 AdditionalData ad;
1432 NiceMock<MockDataInterface> dataIface;
1433 EXPECT_CALL(dataIface, getRawProgressSRC())
1434 .WillOnce(Return(std::vector<uint8_t>{
1435 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1436 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1437 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1438 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1439 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1440
1441 SRC src{entry, ad, dataIface};
1442 EXPECT_TRUE(src.valid());
1443
1444 // Verify that the hex vlue is set at the right hexword
1445 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1446 }
1447