blob: f439bfc08d4c1cb4917122533260ff21ef6afdbd [file] [log] [blame]
Matt Spinler97f7abc2019-11-06 09:40:23 -06001/**
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 */
Matt Spinlerf9bae182019-10-09 13:37:38 -050016#include "extensions/openpower-pels/src.hpp"
Matt Spinler075e5ba2020-02-21 15:46:00 -060017#include "mocks.hpp"
Matt Spinlerf9bae182019-10-09 13:37:38 -050018#include "pel_utils.hpp"
19
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080020#include <fstream>
21
Matt Spinlerf9bae182019-10-09 13:37:38 -050022#include <gtest/gtest.h>
23
24using namespace openpower::pels;
Matt Spinlered046852020-03-13 13:58:15 -050025using ::testing::_;
26using ::testing::InvokeWithoutArgs;
Matt Spinler075e5ba2020-02-21 15:46:00 -060027using ::testing::NiceMock;
28using ::testing::Return;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050029using ::testing::ReturnRef;
Matt Spinlered046852020-03-13 13:58:15 -050030using ::testing::SetArgReferee;
Matt Spinler3bdd0112020-08-27 10:24:34 -050031using ::testing::Throw;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080032namespace fs = std::filesystem;
Matt Spinlerf9bae182019-10-09 13:37:38 -050033
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080034const 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
82class SRCTest : public ::testing::Test
83{
84 protected:
85 static void SetUpTestCase()
86 {
87 char path[] = "/tmp/srctestXXXXXX";
88 regDir = mkdtemp(path);
89 }
90
91 static void TearDownTestCase()
92 {
93 fs::remove_all(regDir);
94 }
95
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
107fs::path SRCTest::regDir{};
108
109TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500110{
Matt Spinler42828bd2019-10-11 10:39:30 -0500111 auto data = pelDataFactory(TestPELType::primarySRCSection);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500112
113 Stream stream{data};
114 SRC src{stream};
115
116 EXPECT_TRUE(src.valid());
117
118 EXPECT_EQ(src.header().id, 0x5053);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600119 EXPECT_EQ(src.header().size, 0x50);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500120 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();
Matt Spinlerbd716f02019-10-15 10:54:11 -0500130 EXPECT_EQ(0x02020255, hexwords[0]);
131 EXPECT_EQ(0x03030310, hexwords[1]);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500132 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
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800150TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500151{
Matt Spinler42828bd2019-10-11 10:39:30 -0500152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500153
154 Stream stream{data};
155 SRC src{stream};
156
157 EXPECT_TRUE(src.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
Matt Spinlerf9bae182019-10-09 13:37:38 -0500159
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}
Matt Spinlerbd716f02019-10-15 10:54:11 -0500185
186// Create an SRC from the message registry
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800187TEST_F(SRCTest, CreateTestNoCallouts)
Matt Spinlerbd716f02019-10-15 10:54:11 -0500188{
189 message::Entry entry;
190 entry.src.type = 0xBD;
191 entry.src.reasonCode = 0xABCD;
192 entry.subsystem = 0x42;
193 entry.src.powerFault = true;
194 entry.src.hexwordADFields = {{5, "TEST1"}, // Not a user defined word
195 {6, "TEST1"},
196 {7, "TEST2"},
197 {8, "TEST3"},
198 {9, "TEST4"}};
199
200 // Values for the SRC words pointed to above
201 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
202 "TEST3=0XDEF", "TEST4=Z"};
203 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600204 NiceMock<MockDataInterface> dataIface;
205
206 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
207
208 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500209
210 EXPECT_TRUE(src.valid());
211 EXPECT_TRUE(src.isPowerFaultEvent());
212 EXPECT_EQ(src.size(), baseSRCSize);
213
214 const auto& hexwords = src.hexwordData();
215
216 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
217 // array index starts at 0 use the math in the [] below to make it easier
218 // to tell what is being accessed.
219 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
220 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
221 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
222 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600223 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500224
225 // Validate more fields here as the code starts filling them in.
226
227 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
228 EXPECT_EQ(hexwords[5 - 2], 0);
229
230 // The user defined hex word fields specifed in the additional data.
231 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
232 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
233 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
234 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
235
236 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
237
238 // No callouts
239 EXPECT_FALSE(src.callouts());
240
241 // May as well spot check the flatten/unflatten
242 std::vector<uint8_t> data;
243 Stream stream{data};
244 src.flatten(stream);
245
246 stream.offset(0);
247 SRC newSRC{stream};
248
249 EXPECT_TRUE(newSRC.valid());
250 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent());
251 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
252 EXPECT_FALSE(newSRC.callouts());
253}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800254
Matt Spinler075e5ba2020-02-21 15:46:00 -0600255// Test when the CCIN string isn't a 4 character number
256TEST_F(SRCTest, BadCCINTest)
257{
258 message::Entry entry;
259 entry.src.type = 0xBD;
260 entry.src.reasonCode = 0xABCD;
261 entry.subsystem = 0x42;
262 entry.src.powerFault = false;
263
264 std::vector<std::string> adData{};
265 AdditionalData ad{adData};
266 NiceMock<MockDataInterface> dataIface;
267
268 // First it isn't a number, then it is too long,
269 // then it is empty.
270 EXPECT_CALL(dataIface, getMotherboardCCIN)
271 .WillOnce(Return("X"))
272 .WillOnce(Return("12345"))
273 .WillOnce(Return(""));
274
275 // The CCIN in the first half should still be 0 each time.
276 {
277 SRC src{entry, ad, dataIface};
278 EXPECT_TRUE(src.valid());
279 const auto& hexwords = src.hexwordData();
280 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
281 }
282
283 {
284 SRC src{entry, ad, dataIface};
285 EXPECT_TRUE(src.valid());
286 const auto& hexwords = src.hexwordData();
287 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
288 }
289
290 {
291 SRC src{entry, ad, dataIface};
292 EXPECT_TRUE(src.valid());
293 const auto& hexwords = src.hexwordData();
294 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
295 }
296}
297
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800298// Test the getErrorDetails function
299TEST_F(SRCTest, MessageSubstitutionTest)
300{
301 auto path = SRCTest::writeData(testRegistry);
302 message::Registry registry{path};
303 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
304
305 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
306 "DURATION=30", "ERRORCODE=0x01ABCDEF"};
307 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600308 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800309
Matt Spinler075e5ba2020-02-21 15:46:00 -0600310 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800311 EXPECT_TRUE(src.valid());
312
313 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
314 ASSERT_TRUE(errorDetails);
315 EXPECT_EQ(
316 errorDetails.value(),
317 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF");
318}
Matt Spinlered046852020-03-13 13:58:15 -0500319
320// Test that an inventory path callout string is
321// converted into the appropriate FRU callout.
322TEST_F(SRCTest, InventoryCalloutTest)
323{
324 message::Entry entry;
325 entry.src.type = 0xBD;
326 entry.src.reasonCode = 0xABCD;
327 entry.subsystem = 0x42;
328 entry.src.powerFault = false;
329
330 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
331 AdditionalData ad{adData};
332 NiceMock<MockDataInterface> dataIface;
333
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500334 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
335 .WillOnce(Return("UTMS-P1"));
336
337 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500338 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500339 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
340 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500341
342 SRC src{entry, ad, dataIface};
343 EXPECT_TRUE(src.valid());
344
345 ASSERT_TRUE(src.callouts());
346
347 EXPECT_EQ(src.callouts()->callouts().size(), 1);
348
349 auto& callout = src.callouts()->callouts().front();
350
351 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500352 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500353
354 auto& fru = callout->fruIdentity();
355
356 EXPECT_EQ(fru->getPN().value(), "1234567");
357 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
358 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
359
360 // flatten and unflatten
361 std::vector<uint8_t> data;
362 Stream stream{data};
363 src.flatten(stream);
364
365 stream.offset(0);
366 SRC newSRC{stream};
367 EXPECT_TRUE(newSRC.valid());
368 ASSERT_TRUE(src.callouts());
369 EXPECT_EQ(src.callouts()->callouts().size(), 1);
370}
371
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500372// Test that when the location code can't be obtained that
Matt Spinlered046852020-03-13 13:58:15 -0500373// a procedure callout is used.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500374TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
375{
376 message::Entry entry;
377 entry.src.type = 0xBD;
378 entry.src.reasonCode = 0xABCD;
379 entry.subsystem = 0x42;
380 entry.src.powerFault = false;
381
382 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
383 AdditionalData ad{adData};
384 NiceMock<MockDataInterface> dataIface;
385
386 auto func = []() {
387 throw sdbusplus::exception::SdBusError(5, "Error");
388 return std::string{};
389 };
390
391 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
392 .Times(1)
393 .WillOnce(InvokeWithoutArgs(func));
394
395 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
396
397 SRC src{entry, ad, dataIface};
398 EXPECT_TRUE(src.valid());
399
400 ASSERT_TRUE(src.callouts());
401
402 EXPECT_EQ(src.callouts()->callouts().size(), 1);
403
404 auto& callout = src.callouts()->callouts().front();
405 EXPECT_EQ(callout->locationCodeSize(), 0);
Matt Spinler717de422020-06-04 13:10:14 -0500406 EXPECT_EQ(callout->priority(), 'H');
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500407
408 auto& fru = callout->fruIdentity();
409
410 EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01");
411 EXPECT_FALSE(fru->getPN());
412 EXPECT_FALSE(fru->getSN());
413 EXPECT_FALSE(fru->getCCIN());
414
415 // flatten and unflatten
416 std::vector<uint8_t> data;
417 Stream stream{data};
418 src.flatten(stream);
419
420 stream.offset(0);
421 SRC newSRC{stream};
422 EXPECT_TRUE(newSRC.valid());
423 ASSERT_TRUE(src.callouts());
424 EXPECT_EQ(src.callouts()->callouts().size(), 1);
425}
426
427// Test that when the VPD can't be obtained that
428// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500429TEST_F(SRCTest, InventoryCalloutNoVPDTest)
430{
431 message::Entry entry;
432 entry.src.type = 0xBD;
433 entry.src.reasonCode = 0xABCD;
434 entry.subsystem = 0x42;
435 entry.src.powerFault = false;
436
437 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
438 AdditionalData ad{adData};
439 NiceMock<MockDataInterface> dataIface;
440
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500441 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
442 .Times(1)
443 .WillOnce(Return("UTMS-P10"));
444
Matt Spinlered046852020-03-13 13:58:15 -0500445 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
446
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500447 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500448 .Times(1)
449 .WillOnce(InvokeWithoutArgs(func));
450
451 SRC src{entry, ad, dataIface};
452 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500453 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500454 EXPECT_EQ(src.callouts()->callouts().size(), 1);
455
456 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500457 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500458 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500459
460 auto& fru = callout->fruIdentity();
461
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500462 EXPECT_EQ(fru->getPN(), "");
463 EXPECT_EQ(fru->getCCIN(), "");
464 EXPECT_EQ(fru->getSN(), "");
465 EXPECT_FALSE(fru->getMaintProc());
466
Matt Spinlered046852020-03-13 13:58:15 -0500467 // flatten and unflatten
468 std::vector<uint8_t> data;
469 Stream stream{data};
470 src.flatten(stream);
471
472 stream.offset(0);
473 SRC newSRC{stream};
474 EXPECT_TRUE(newSRC.valid());
475 ASSERT_TRUE(src.callouts());
476 EXPECT_EQ(src.callouts()->callouts().size(), 1);
477}
Matt Spinler03984582020-04-09 13:17:58 -0500478
479TEST_F(SRCTest, RegistryCalloutTest)
480{
481 message::Entry entry;
482 entry.src.type = 0xBD;
483 entry.src.reasonCode = 0xABCD;
484 entry.subsystem = 0x42;
485 entry.src.powerFault = false;
486
487 entry.callouts = R"(
488 [
489 {
490 "System": "systemA",
491 "CalloutList":
492 [
493 {
494 "Priority": "high",
495 "SymbolicFRU": "service_docs"
496 },
497 {
498 "Priority": "medium",
499 "Procedure": "no_vpd_for_fru"
500 }
501 ]
502 },
503 {
504 "System": "systemB",
505 "CalloutList":
506 [
507 {
508 "Priority": "high",
509 "LocCode": "P0-C8",
510 "SymbolicFRUTrusted": "service_docs"
511 },
512 {
513 "Priority": "medium",
514 "SymbolicFRUTrusted": "service_docs"
515 }
516 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500517 },
518 {
519 "System": "systemC",
520 "CalloutList":
521 [
522 {
523 "Priority": "high",
524 "LocCode": "P0-C8"
525 },
526 {
527 "Priority": "medium",
528 "LocCode": "P0-C9"
529 }
530 ]
Matt Spinler03984582020-04-09 13:17:58 -0500531 }
532 ])"_json;
533
534 {
535 // Call out a symbolic FRU and a procedure
536 AdditionalData ad;
537 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500538 std::vector<std::string> names{"systemA"};
539
540 EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
Matt Spinler03984582020-04-09 13:17:58 -0500541
542 SRC src{entry, ad, dataIface};
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(), "BMCSP01");
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;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500574 std::vector<std::string> names{"systemB"};
575
Matt Spinleraf191c72020-06-04 11:35:13 -0500576 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500577 EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
Matt Spinler03984582020-04-09 13:17:58 -0500578
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 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500607
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(ReturnRef(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
Matt Spinler2f9225a2020-08-05 12:58:49 -0500622 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500623 .WillOnce(Return(
624 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
625
Matt Spinler2f9225a2020-08-05 12:58:49 -0500626 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500627 .WillOnce(Return(
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(DoAll(SetArgReferee<1>("1234567"),
637 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(DoAll(SetArgReferee<1>("2345678"),
647 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 }
Matt Spinler03984582020-04-09 13:17:58 -0500671}
Matt Spinler717de422020-06-04 13:10:14 -0500672
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500673// 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.
676TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
677{
678 message::Entry entry;
679 entry.src.type = 0xBD;
680 entry.src.reasonCode = 0xABCD;
681 entry.subsystem = 0x42;
682 entry.src.powerFault = false;
683
684 entry.callouts = R"(
685 [{
686 "CalloutList":
687 [
688 {
689 "Priority": "high",
690 "SymbolicFRUTrusted": "service_docs",
691 "UseInventoryLocCode": true
692 },
693 {
694 "Priority": "medium",
695 "LocCode": "P0-C8",
696 "SymbolicFRUTrusted": "pwrsply"
697 }
698 ]
699 }])"_json;
700
701 {
702 // The location code for the first symbolic FRU callout will
703 // come from this inventory path since UseInventoryLocCode is set.
704 // In this case there will be no normal FRU callout for the motherboard.
705 std::vector<std::string> adData{"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(ReturnRef(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(ReturnRef(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
Matt Spinler717de422020-06-04 13:10:14 -0500778// Test looking up device path fails in the callout jSON.
779TEST_F(SRCTest, DevicePathCalloutTest)
780{
781 message::Entry entry;
782 entry.src.type = 0xBD;
783 entry.src.reasonCode = 0xABCD;
784 entry.subsystem = 0x42;
785 entry.src.powerFault = false;
786
787 const auto calloutJSON = R"(
788 {
789 "I2C":
790 {
791 "14":
792 {
793 "114":
794 {
795 "Callouts":[
796 {
797 "Name": "/chassis/motherboard/cpu0",
798 "LocationCode": "P1-C40",
799 "Priority": "H"
800 },
801 {
802 "Name": "/chassis/motherboard",
803 "LocationCode": "P1",
804 "Priority": "M"
805 },
806 {
807 "Name": "/chassis/motherboard/bmc",
808 "LocationCode": "P1-C15",
809 "Priority": "L"
810 }
811 ],
812 "Dest": "proc 0 target"
813 }
814 }
815 }
816 })";
817
818 auto dataPath = getPELReadOnlyDataPath();
819 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
820 file << calloutJSON;
821 file.close();
822
823 NiceMock<MockDataInterface> dataIface;
824 std::vector<std::string> names{"systemA"};
825
826 EXPECT_CALL(dataIface, getSystemNames)
827 .Times(5)
828 .WillRepeatedly(ReturnRef(names));
829
Matt Spinler2f9225a2020-08-05 12:58:49 -0500830 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500831 .Times(3)
832 .WillRepeatedly(
833 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
834
Matt Spinler2f9225a2020-08-05 12:58:49 -0500835 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500836 .Times(3)
837 .WillRepeatedly(
838 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
839
Matt Spinler2f9225a2020-08-05 12:58:49 -0500840 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500841 .Times(3)
842 .WillRepeatedly(
843 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
844
845 EXPECT_CALL(dataIface,
846 getLocationCode(
847 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"))
848 .Times(3)
849 .WillRepeatedly(Return("Ufcs-P1-C40"));
850 EXPECT_CALL(
851 dataIface,
852 getLocationCode("/xyz/openbmc_project/inventory/chassis/motherboard"))
853 .Times(3)
854 .WillRepeatedly(Return("Ufcs-P1"));
855 EXPECT_CALL(dataIface,
856 getLocationCode(
857 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"))
858 .Times(3)
859 .WillRepeatedly(Return("Ufcs-P1-C15"));
860
861 EXPECT_CALL(
862 dataIface,
863 getHWCalloutFields(
864 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
865 .Times(3)
866 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
867 SetArgReferee<2>("CCCC"),
868 SetArgReferee<3>("123456789ABC")));
869 EXPECT_CALL(
870 dataIface,
871 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
872 _, _, _))
873 .Times(3)
874 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
875 SetArgReferee<2>("MMMM"),
876 SetArgReferee<3>("CBA987654321")));
877 EXPECT_CALL(
878 dataIface,
879 getHWCalloutFields(
880 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
881 .Times(3)
882 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
883 SetArgReferee<2>("BBBB"),
884 SetArgReferee<3>("C123456789AB")));
885
886 // Call this below with different AdditionalData values that
887 // result in the same callouts.
888 auto checkCallouts = [&entry, &dataIface](const auto& items) {
889 AdditionalData ad{items};
890 SRC src{entry, ad, dataIface};
891
892 ASSERT_TRUE(src.callouts());
893 auto& callouts = src.callouts()->callouts();
894
895 ASSERT_EQ(callouts.size(), 3);
896
897 {
898 EXPECT_EQ(callouts[0]->priority(), 'H');
899 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
900
901 auto& fru = callouts[0]->fruIdentity();
902 EXPECT_EQ(fru->getPN().value(), "1234567");
903 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
904 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
905 }
906 {
907 EXPECT_EQ(callouts[1]->priority(), 'M');
908 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
909
910 auto& fru = callouts[1]->fruIdentity();
911 EXPECT_EQ(fru->getPN().value(), "7654321");
912 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
913 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
914 }
915 {
916 EXPECT_EQ(callouts[2]->priority(), 'L');
917 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
918
919 auto& fru = callouts[2]->fruIdentity();
920 EXPECT_EQ(fru->getPN().value(), "7123456");
921 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
922 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
923 }
924 };
925
926 {
927 // Callouts based on the device path
928 std::vector<std::string> items{
929 "CALLOUT_ERRNO=5",
930 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
931 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
932
933 checkCallouts(items);
934 }
935
936 {
937 // Callouts based on the I2C bus and address
938 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
939 "CALLOUT_IIC_ADDR=0x72"};
940 checkCallouts(items);
941 }
942
943 {
944 // Also based on I2C bus and address, but with bus = /dev/i2c-14
945 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
946 "CALLOUT_IIC_ADDR=0x72"};
947 checkCallouts(items);
948 }
949
950 {
951 // Callout not found
952 std::vector<std::string> items{
953 "CALLOUT_ERRNO=5",
954 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
955 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
956
957 AdditionalData ad{items};
958 SRC src{entry, ad, dataIface};
959
960 EXPECT_FALSE(src.callouts());
961 ASSERT_EQ(src.getDebugData().size(), 1);
962 EXPECT_EQ(src.getDebugData()[0],
963 "Problem looking up I2C callouts on 24 18: "
964 "[json.exception.out_of_range.403] key '24' not found");
965 }
966
967 {
968 // Callout not found
969 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
970 "CALLOUT_IIC_ADDR=0x99"};
971 AdditionalData ad{items};
972 SRC src{entry, ad, dataIface};
973
974 EXPECT_FALSE(src.callouts());
975 ASSERT_EQ(src.getDebugData().size(), 1);
976 EXPECT_EQ(src.getDebugData()[0],
977 "Problem looking up I2C callouts on 22 153: "
978 "[json.exception.out_of_range.403] key '22' not found");
979 }
980
981 fs::remove_all(dataPath);
982}
Matt Spinler3bdd0112020-08-27 10:24:34 -0500983
984// Test when callouts are passed in via JSON
985TEST_F(SRCTest, JsonCalloutsTest)
986{
987 const auto jsonCallouts = R"(
988 [
989 {
990 "LocationCode": "P0-C1",
991 "Priority": "H",
992 "MRUs": [
993 {
994 "ID": 42,
995 "Priority": "H"
996 },
997 {
998 "ID": 43,
999 "Priority": "M"
1000 }
1001 ]
1002 },
1003 {
1004 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1005 "Priority": "M",
1006 "Guarded": true,
1007 "Deconfigured": true
1008 },
1009 {
1010 "Procedure": "PROCEDU",
1011 "Priority": "A"
1012 },
1013 {
1014 "SymbolicFRU": "TRUSTED",
1015 "Priority": "B",
1016 "TrustedLocationCode": true,
1017 "LocationCode": "P1-C23"
1018 },
1019 {
1020 "SymbolicFRU": "FRUTST1",
1021 "Priority": "C",
1022 "LocationCode": "P1-C24"
1023 },
1024 {
1025 "SymbolicFRU": "FRUTST2LONG",
1026 "Priority": "L"
1027 }
1028 ]
1029 )"_json;
1030
1031 message::Entry entry;
1032 entry.src.type = 0xBD;
1033 entry.src.reasonCode = 0xABCD;
1034 entry.subsystem = 0x42;
1035 entry.src.powerFault = false;
1036
1037 AdditionalData ad;
1038 NiceMock<MockDataInterface> dataIface;
1039
1040 // Callout 0 mock calls
1041 {
1042 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1043 .Times(1)
1044 .WillOnce(Return("UXXX-P0-C1"));
1045 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1046 .Times(1)
1047 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1048 EXPECT_CALL(
1049 dataIface,
1050 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1051 .Times(1)
1052 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1053 SetArgReferee<2>("CCCC"),
1054 SetArgReferee<3>("123456789ABC")));
1055 }
1056 // Callout 1 mock calls
1057 {
1058 EXPECT_CALL(dataIface,
1059 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1060 .WillOnce(Return("UYYY-P5"));
1061 EXPECT_CALL(
1062 dataIface,
1063 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1064 .Times(1)
1065 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1066 SetArgReferee<2>("DDDD"),
1067 SetArgReferee<3>("23456789ABCD")));
1068 }
1069 // Callout 3 mock calls
1070 {
1071 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1072 .Times(1)
1073 .WillOnce(Return("UXXX-P1-C23"));
1074 }
1075 // Callout 4 mock calls
1076 {
1077 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1078 .Times(1)
1079 .WillOnce(Return("UXXX-P1-C24"));
1080 }
1081
1082 SRC src{entry, ad, jsonCallouts, dataIface};
1083 ASSERT_TRUE(src.callouts());
1084
Matt Spinlerafa2c792020-08-27 11:01:39 -05001085 // Check the guarded and deconfigured flags
1086 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1087
Matt Spinler3bdd0112020-08-27 10:24:34 -05001088 const auto& callouts = src.callouts()->callouts();
1089 ASSERT_EQ(callouts.size(), 6);
1090
1091 // Check callout 0
1092 {
1093 EXPECT_EQ(callouts[0]->priority(), 'H');
1094 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1095
1096 auto& fru = callouts[0]->fruIdentity();
1097 EXPECT_EQ(fru->getPN().value(), "1234567");
1098 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1099 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1100 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001101
1102 auto& mruCallouts = callouts[0]->mru();
1103 ASSERT_TRUE(mruCallouts);
1104 auto& mrus = mruCallouts->mrus();
1105 ASSERT_EQ(mrus.size(), 2);
1106 EXPECT_EQ(mrus[0].id, 42);
1107 EXPECT_EQ(mrus[0].priority, 'H');
1108 EXPECT_EQ(mrus[1].id, 43);
1109 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001110 }
1111
1112 // Check callout 1
1113 {
1114 EXPECT_EQ(callouts[1]->priority(), 'M');
1115 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1116
1117 auto& fru = callouts[1]->fruIdentity();
1118 EXPECT_EQ(fru->getPN().value(), "2345678");
1119 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1120 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1121 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1122 }
1123
1124 // Check callout 2
1125 {
1126 EXPECT_EQ(callouts[2]->priority(), 'A');
1127 EXPECT_EQ(callouts[2]->locationCode(), "");
1128
1129 auto& fru = callouts[2]->fruIdentity();
1130 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1131 EXPECT_EQ(fru->failingComponentType(),
1132 src::FRUIdentity::maintenanceProc);
1133 }
1134
1135 // Check callout 3
1136 {
1137 EXPECT_EQ(callouts[3]->priority(), 'B');
1138 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1139
1140 auto& fru = callouts[3]->fruIdentity();
1141 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1142 EXPECT_EQ(fru->failingComponentType(),
1143 src::FRUIdentity::symbolicFRUTrustedLocCode);
1144 }
1145
1146 // Check callout 4
1147 {
1148 EXPECT_EQ(callouts[4]->priority(), 'C');
1149 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1150
1151 auto& fru = callouts[4]->fruIdentity();
1152 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1153 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1154 }
1155
1156 // Check callout 5
1157 {
1158 EXPECT_EQ(callouts[5]->priority(), 'L');
1159 EXPECT_EQ(callouts[5]->locationCode(), "");
1160
1161 auto& fru = callouts[5]->fruIdentity();
1162 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1163 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1164 }
1165
1166 // Check that it didn't find any errors
1167 const auto& data = src.getDebugData();
1168 EXPECT_TRUE(data.empty());
1169}
1170
1171TEST_F(SRCTest, JsonBadCalloutsTest)
1172{
1173 // The first call will have a Throw in a mock call.
1174 // The second will have a different Throw in a mock call.
1175 // The others have issues with the Priority field.
1176 const auto jsonCallouts = R"(
1177 [
1178 {
1179 "LocationCode": "P0-C1",
1180 "Priority": "H"
1181 },
1182 {
1183 "LocationCode": "P0-C2",
1184 "Priority": "H"
1185 },
1186 {
1187 "LocationCode": "P0-C3"
1188 },
1189 {
1190 "LocationCode": "P0-C4",
1191 "Priority": "X"
1192 }
1193 ]
1194 )"_json;
1195
1196 message::Entry entry;
1197 entry.src.type = 0xBD;
1198 entry.src.reasonCode = 0xABCD;
1199 entry.subsystem = 0x42;
1200 entry.src.powerFault = false;
1201
1202 AdditionalData ad;
1203 NiceMock<MockDataInterface> dataIface;
1204
1205 // Callout 0 mock calls
1206 // Expand location code will fail, so the unexpanded location
1207 // code should show up in the callout instead.
1208 {
1209 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1210 .WillOnce(Throw(std::runtime_error("Fail")));
1211
1212 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1213 .Times(1)
1214 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1215 EXPECT_CALL(
1216 dataIface,
1217 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1218 .Times(1)
1219 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1220 SetArgReferee<2>("CCCC"),
1221 SetArgReferee<3>("123456789ABC")));
1222 }
1223
1224 // Callout 1 mock calls
1225 // getInventoryFromLocCode will fail
1226 {
1227 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1228 .Times(1)
1229 .WillOnce(Return("UXXX-P0-C2"));
1230
1231 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1232 .Times(1)
1233 .WillOnce(Throw(std::runtime_error("Fail")));
1234 }
1235
1236 SRC src{entry, ad, jsonCallouts, dataIface};
1237
1238 ASSERT_TRUE(src.callouts());
1239
1240 const auto& callouts = src.callouts()->callouts();
1241
1242 // Only the first callout was successful
1243 ASSERT_EQ(callouts.size(), 1);
1244
1245 {
1246 EXPECT_EQ(callouts[0]->priority(), 'H');
1247 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1248
1249 auto& fru = callouts[0]->fruIdentity();
1250 EXPECT_EQ(fru->getPN().value(), "1234567");
1251 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1252 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1253 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1254 }
1255
1256 const auto& data = src.getDebugData();
1257 ASSERT_EQ(data.size(), 4);
1258 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1259 EXPECT_STREQ(data[1].c_str(),
1260 "Failed extracting callout data from JSON: Unable to "
1261 "get inventory path from location code: P0-C2: Fail");
1262 EXPECT_STREQ(data[2].c_str(),
1263 "Failed extracting callout data from JSON: "
1264 "[json.exception.out_of_range.403] key 'Priority' not found");
1265 EXPECT_STREQ(data[3].c_str(),
1266 "Failed extracting callout data from JSON: Invalid "
1267 "priority 'X' found in JSON callout");
1268}