blob: 32facd490ba4d924967bb0d7a64922b0dda6e426 [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 Spinlered046852020-03-13 13:58:15 -050029using ::testing::SetArgReferee;
Matt Spinler3bdd0112020-08-27 10:24:34 -050030using ::testing::Throw;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080031namespace fs = std::filesystem;
Matt Spinlerf9bae182019-10-09 13:37:38 -050032
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080033const auto testRegistry = R"(
34{
35"PELs":
36[
37 {
38 "Name": "xyz.openbmc_project.Error.Test",
39 "Subsystem": "bmc_firmware",
40 "SRC":
41 {
42 "ReasonCode": "0xABCD",
43 "Words6To9":
44 {
45 "6":
46 {
47 "Description": "Component ID",
48 "AdditionalDataPropSource": "COMPID"
49 },
50 "7":
51 {
52 "Description": "Failure count",
53 "AdditionalDataPropSource": "FREQUENCY"
54 },
55 "8":
56 {
57 "Description": "Time period",
58 "AdditionalDataPropSource": "DURATION"
59 },
60 "9":
61 {
62 "Description": "Error code",
63 "AdditionalDataPropSource": "ERRORCODE"
64 }
65 }
66 },
67 "Documentation":
68 {
69 "Description": "A Component Fault",
70 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
71 "MessageArgSources":
72 [
73 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
74 ]
75 }
76 }
77]
78}
79)";
80
81class SRCTest : public ::testing::Test
82{
83 protected:
84 static void SetUpTestCase()
85 {
86 char path[] = "/tmp/srctestXXXXXX";
87 regDir = mkdtemp(path);
88 }
89
90 static void TearDownTestCase()
91 {
92 fs::remove_all(regDir);
93 }
94
95 static std::string writeData(const char* data)
96 {
97 fs::path path = regDir / "registry.json";
98 std::ofstream stream{path};
99 stream << data;
100 return path;
101 }
102
103 static fs::path regDir;
104};
105
106fs::path SRCTest::regDir{};
107
108TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500109{
Matt Spinler42828bd2019-10-11 10:39:30 -0500110 auto data = pelDataFactory(TestPELType::primarySRCSection);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500111
112 Stream stream{data};
113 SRC src{stream};
114
115 EXPECT_TRUE(src.valid());
116
117 EXPECT_EQ(src.header().id, 0x5053);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600118 EXPECT_EQ(src.header().size, 0x50);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500119 EXPECT_EQ(src.header().version, 0x01);
120 EXPECT_EQ(src.header().subType, 0x01);
121 EXPECT_EQ(src.header().componentID, 0x0202);
122
123 EXPECT_EQ(src.version(), 0x02);
124 EXPECT_EQ(src.flags(), 0x00);
125 EXPECT_EQ(src.hexWordCount(), 9);
126 EXPECT_EQ(src.size(), 0x48);
127
128 const auto& hexwords = src.hexwordData();
Matt Spinlerbd716f02019-10-15 10:54:11 -0500129 EXPECT_EQ(0x02020255, hexwords[0]);
130 EXPECT_EQ(0x03030310, hexwords[1]);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500131 EXPECT_EQ(0x04040404, hexwords[2]);
132 EXPECT_EQ(0x05050505, hexwords[3]);
133 EXPECT_EQ(0x06060606, hexwords[4]);
134 EXPECT_EQ(0x07070707, hexwords[5]);
135 EXPECT_EQ(0x08080808, hexwords[6]);
136 EXPECT_EQ(0x09090909, hexwords[7]);
137
138 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
139 EXPECT_FALSE(src.callouts());
140
141 // Flatten
142 std::vector<uint8_t> newData;
143 Stream newStream{newData};
144
145 src.flatten(newStream);
146 EXPECT_EQ(data, newData);
147}
148
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800149TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500150{
Matt Spinler42828bd2019-10-11 10:39:30 -0500151 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500152
153 Stream stream{data};
154 SRC src{stream};
155
156 EXPECT_TRUE(src.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500157 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
Matt Spinlerf9bae182019-10-09 13:37:38 -0500158
159 // Spot check the SRC fields, but they're the same as above
160 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
161
162 // There should be 2 callouts
163 const auto& calloutsSection = src.callouts();
164 ASSERT_TRUE(calloutsSection);
165 const auto& callouts = calloutsSection->callouts();
166 EXPECT_EQ(callouts.size(), 2);
167
168 // spot check that each callout has the right substructures
169 EXPECT_TRUE(callouts.front()->fruIdentity());
170 EXPECT_FALSE(callouts.front()->pceIdentity());
171 EXPECT_FALSE(callouts.front()->mru());
172
173 EXPECT_TRUE(callouts.back()->fruIdentity());
174 EXPECT_TRUE(callouts.back()->pceIdentity());
175 EXPECT_TRUE(callouts.back()->mru());
176
177 // Flatten
178 std::vector<uint8_t> newData;
179 Stream newStream{newData};
180
181 src.flatten(newStream);
182 EXPECT_EQ(data, newData);
183}
Matt Spinlerbd716f02019-10-15 10:54:11 -0500184
185// Create an SRC from the message registry
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800186TEST_F(SRCTest, CreateTestNoCallouts)
Matt Spinlerbd716f02019-10-15 10:54:11 -0500187{
188 message::Entry entry;
189 entry.src.type = 0xBD;
190 entry.src.reasonCode = 0xABCD;
191 entry.subsystem = 0x42;
192 entry.src.powerFault = true;
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800193 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"}}};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500199
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
Vijay Lobof3702bb2021-04-09 15:10:19 -0500255// Create an SRC to test POWER_THERMAL_CRITICAL_FAULT set to TRUE
256// sets the power fault bit in SRC
257TEST_F(SRCTest, PowerFaultTest)
258{
259 message::Entry entry;
260 entry.src.type = 0xBD;
261 entry.src.reasonCode = 0xABCD;
262 entry.subsystem = 0x42;
263 entry.src.powerFault = false;
264
265 // Values for the SRC words pointed to above
266 std::vector<std::string> adData{"POWER_THERMAL_CRITICAL_FAULT=TRUE",
267 "TEST2=12345678", "TEST3=0XDEF", "TEST4=Z"};
268 AdditionalData ad{adData};
269 NiceMock<MockDataInterface> dataIface;
270
271 SRC src{entry, ad, dataIface};
272
273 EXPECT_TRUE(src.valid());
274 EXPECT_TRUE(src.isPowerFaultEvent());
275 EXPECT_EQ(src.size(), baseSRCSize);
276}
277
Matt Spinler075e5ba2020-02-21 15:46:00 -0600278// Test when the CCIN string isn't a 4 character number
279TEST_F(SRCTest, BadCCINTest)
280{
281 message::Entry entry;
282 entry.src.type = 0xBD;
283 entry.src.reasonCode = 0xABCD;
284 entry.subsystem = 0x42;
285 entry.src.powerFault = false;
286
287 std::vector<std::string> adData{};
288 AdditionalData ad{adData};
289 NiceMock<MockDataInterface> dataIface;
290
291 // First it isn't a number, then it is too long,
292 // then it is empty.
293 EXPECT_CALL(dataIface, getMotherboardCCIN)
294 .WillOnce(Return("X"))
295 .WillOnce(Return("12345"))
296 .WillOnce(Return(""));
297
298 // The CCIN in the first half should still be 0 each time.
299 {
300 SRC src{entry, ad, dataIface};
301 EXPECT_TRUE(src.valid());
302 const auto& hexwords = src.hexwordData();
303 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
304 }
305
306 {
307 SRC src{entry, ad, dataIface};
308 EXPECT_TRUE(src.valid());
309 const auto& hexwords = src.hexwordData();
310 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
311 }
312
313 {
314 SRC src{entry, ad, dataIface};
315 EXPECT_TRUE(src.valid());
316 const auto& hexwords = src.hexwordData();
317 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
318 }
319}
320
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800321// Test the getErrorDetails function
322TEST_F(SRCTest, MessageSubstitutionTest)
323{
324 auto path = SRCTest::writeData(testRegistry);
325 message::Registry registry{path};
326 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
327
328 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
329 "DURATION=30", "ERRORCODE=0x01ABCDEF"};
330 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600331 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800332
Matt Spinler075e5ba2020-02-21 15:46:00 -0600333 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800334 EXPECT_TRUE(src.valid());
335
336 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
337 ASSERT_TRUE(errorDetails);
338 EXPECT_EQ(
339 errorDetails.value(),
340 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF");
341}
Matt Spinlered046852020-03-13 13:58:15 -0500342// Test that an inventory path callout string is
343// converted into the appropriate FRU callout.
344TEST_F(SRCTest, InventoryCalloutTest)
345{
346 message::Entry entry;
347 entry.src.type = 0xBD;
348 entry.src.reasonCode = 0xABCD;
349 entry.subsystem = 0x42;
350 entry.src.powerFault = false;
351
352 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
353 AdditionalData ad{adData};
354 NiceMock<MockDataInterface> dataIface;
355
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500356 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
357 .WillOnce(Return("UTMS-P1"));
358
359 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500360 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500361 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
362 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500363
364 SRC src{entry, ad, dataIface};
365 EXPECT_TRUE(src.valid());
366
367 ASSERT_TRUE(src.callouts());
368
369 EXPECT_EQ(src.callouts()->callouts().size(), 1);
370
371 auto& callout = src.callouts()->callouts().front();
372
373 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500374 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500375
376 auto& fru = callout->fruIdentity();
377
378 EXPECT_EQ(fru->getPN().value(), "1234567");
379 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
380 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
381
382 // flatten and unflatten
383 std::vector<uint8_t> data;
384 Stream stream{data};
385 src.flatten(stream);
386
387 stream.offset(0);
388 SRC newSRC{stream};
389 EXPECT_TRUE(newSRC.valid());
390 ASSERT_TRUE(src.callouts());
391 EXPECT_EQ(src.callouts()->callouts().size(), 1);
392}
393
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500394// Test that when the location code can't be obtained that
Matt Spinlered046852020-03-13 13:58:15 -0500395// a procedure callout is used.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500396TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
397{
398 message::Entry entry;
399 entry.src.type = 0xBD;
400 entry.src.reasonCode = 0xABCD;
401 entry.subsystem = 0x42;
402 entry.src.powerFault = false;
403
404 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
405 AdditionalData ad{adData};
406 NiceMock<MockDataInterface> dataIface;
407
408 auto func = []() {
409 throw sdbusplus::exception::SdBusError(5, "Error");
410 return std::string{};
411 };
412
413 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
414 .Times(1)
415 .WillOnce(InvokeWithoutArgs(func));
416
417 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
418
419 SRC src{entry, ad, dataIface};
420 EXPECT_TRUE(src.valid());
421
422 ASSERT_TRUE(src.callouts());
423
424 EXPECT_EQ(src.callouts()->callouts().size(), 1);
425
426 auto& callout = src.callouts()->callouts().front();
427 EXPECT_EQ(callout->locationCodeSize(), 0);
Matt Spinler717de422020-06-04 13:10:14 -0500428 EXPECT_EQ(callout->priority(), 'H');
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500429
430 auto& fru = callout->fruIdentity();
431
432 EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01");
433 EXPECT_FALSE(fru->getPN());
434 EXPECT_FALSE(fru->getSN());
435 EXPECT_FALSE(fru->getCCIN());
436
437 // flatten and unflatten
438 std::vector<uint8_t> data;
439 Stream stream{data};
440 src.flatten(stream);
441
442 stream.offset(0);
443 SRC newSRC{stream};
444 EXPECT_TRUE(newSRC.valid());
445 ASSERT_TRUE(src.callouts());
446 EXPECT_EQ(src.callouts()->callouts().size(), 1);
447}
448
449// Test that when the VPD can't be obtained that
450// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500451TEST_F(SRCTest, InventoryCalloutNoVPDTest)
452{
453 message::Entry entry;
454 entry.src.type = 0xBD;
455 entry.src.reasonCode = 0xABCD;
456 entry.subsystem = 0x42;
457 entry.src.powerFault = false;
458
459 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
460 AdditionalData ad{adData};
461 NiceMock<MockDataInterface> dataIface;
462
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500463 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
464 .Times(1)
465 .WillOnce(Return("UTMS-P10"));
466
Matt Spinlered046852020-03-13 13:58:15 -0500467 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
468
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500469 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500470 .Times(1)
471 .WillOnce(InvokeWithoutArgs(func));
472
473 SRC src{entry, ad, dataIface};
474 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500475 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500476 EXPECT_EQ(src.callouts()->callouts().size(), 1);
477
478 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500479 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500480 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500481
482 auto& fru = callout->fruIdentity();
483
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500484 EXPECT_EQ(fru->getPN(), "");
485 EXPECT_EQ(fru->getCCIN(), "");
486 EXPECT_EQ(fru->getSN(), "");
487 EXPECT_FALSE(fru->getMaintProc());
488
Matt Spinlered046852020-03-13 13:58:15 -0500489 // flatten and unflatten
490 std::vector<uint8_t> data;
491 Stream stream{data};
492 src.flatten(stream);
493
494 stream.offset(0);
495 SRC newSRC{stream};
496 EXPECT_TRUE(newSRC.valid());
497 ASSERT_TRUE(src.callouts());
498 EXPECT_EQ(src.callouts()->callouts().size(), 1);
499}
Matt Spinler03984582020-04-09 13:17:58 -0500500
501TEST_F(SRCTest, RegistryCalloutTest)
502{
503 message::Entry entry;
504 entry.src.type = 0xBD;
505 entry.src.reasonCode = 0xABCD;
506 entry.subsystem = 0x42;
507 entry.src.powerFault = false;
508
509 entry.callouts = R"(
510 [
511 {
512 "System": "systemA",
513 "CalloutList":
514 [
515 {
516 "Priority": "high",
517 "SymbolicFRU": "service_docs"
518 },
519 {
520 "Priority": "medium",
521 "Procedure": "no_vpd_for_fru"
522 }
523 ]
524 },
525 {
526 "System": "systemB",
527 "CalloutList":
528 [
529 {
530 "Priority": "high",
531 "LocCode": "P0-C8",
532 "SymbolicFRUTrusted": "service_docs"
533 },
534 {
535 "Priority": "medium",
536 "SymbolicFRUTrusted": "service_docs"
537 }
538 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500539 },
540 {
541 "System": "systemC",
542 "CalloutList":
543 [
544 {
545 "Priority": "high",
546 "LocCode": "P0-C8"
547 },
548 {
549 "Priority": "medium",
550 "LocCode": "P0-C9"
551 }
552 ]
Matt Spinler03984582020-04-09 13:17:58 -0500553 }
554 ])"_json;
555
556 {
557 // Call out a symbolic FRU and a procedure
558 AdditionalData ad;
559 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500560 std::vector<std::string> names{"systemA"};
561
Matt Spinler1ab66962020-10-29 13:21:44 -0500562 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500563
564 SRC src{entry, ad, dataIface};
565
566 auto& callouts = src.callouts()->callouts();
567 ASSERT_EQ(callouts.size(), 2);
568
569 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
570 EXPECT_EQ(callouts[0]->priority(), 'H');
571
572 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
573 EXPECT_EQ(callouts[1]->priority(), 'M');
574
575 auto& fru1 = callouts[0]->fruIdentity();
576 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
577 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
578 EXPECT_FALSE(fru1->getMaintProc());
579 EXPECT_FALSE(fru1->getSN());
580 EXPECT_FALSE(fru1->getCCIN());
581
582 auto& fru2 = callouts[1]->fruIdentity();
583 EXPECT_EQ(fru2->getMaintProc().value(), "BMCSP01");
584 EXPECT_EQ(fru2->failingComponentType(),
585 src::FRUIdentity::maintenanceProc);
586 EXPECT_FALSE(fru2->getPN());
587 EXPECT_FALSE(fru2->getSN());
588 EXPECT_FALSE(fru2->getCCIN());
589 }
590
591 {
592 // Call out a trusted symbolic FRU with a location code, and
593 // another one without.
594 AdditionalData ad;
595 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500596 std::vector<std::string> names{"systemB"};
597
Matt Spinleraf191c72020-06-04 11:35:13 -0500598 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500599 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500600
601 SRC src{entry, ad, dataIface};
602
603 auto& callouts = src.callouts()->callouts();
604 EXPECT_EQ(callouts.size(), 2);
605
606 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
607 EXPECT_EQ(callouts[0]->priority(), 'H');
608
609 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
610 EXPECT_EQ(callouts[1]->priority(), 'M');
611
612 auto& fru1 = callouts[0]->fruIdentity();
613 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
614 EXPECT_EQ(fru1->failingComponentType(),
615 src::FRUIdentity::symbolicFRUTrustedLocCode);
616 EXPECT_FALSE(fru1->getMaintProc());
617 EXPECT_FALSE(fru1->getSN());
618 EXPECT_FALSE(fru1->getCCIN());
619
620 // It asked for a trusted symbolic FRU, but no location code
621 // was provided so it is switched back to a normal one
622 auto& fru2 = callouts[1]->fruIdentity();
623 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
624 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
625 EXPECT_FALSE(fru2->getMaintProc());
626 EXPECT_FALSE(fru2->getSN());
627 EXPECT_FALSE(fru2->getCCIN());
628 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500629
630 {
631 // Two hardware callouts
632 AdditionalData ad;
633 NiceMock<MockDataInterface> dataIface;
634 std::vector<std::string> names{"systemC"};
635
Matt Spinler1ab66962020-10-29 13:21:44 -0500636 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500637
638 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
639 .WillOnce(Return("UXXX-P0-C8"));
640
641 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
642 .WillOnce(Return("UXXX-P0-C9"));
643
Matt Spinler2f9225a2020-08-05 12:58:49 -0500644 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500645 .WillOnce(Return(
646 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
647
Matt Spinler2f9225a2020-08-05 12:58:49 -0500648 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500649 .WillOnce(Return(
650 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
651
652 EXPECT_CALL(
653 dataIface,
654 getHWCalloutFields(
655 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
656 _))
657 .Times(1)
658 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
659 SetArgReferee<2>("CCCC"),
660 SetArgReferee<3>("123456789ABC")));
661
662 EXPECT_CALL(
663 dataIface,
664 getHWCalloutFields(
665 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
666 _))
667 .Times(1)
668 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
669 SetArgReferee<2>("DDDD"),
670 SetArgReferee<3>("23456789ABCD")));
671
672 SRC src{entry, ad, dataIface};
673
674 auto& callouts = src.callouts()->callouts();
675 EXPECT_EQ(callouts.size(), 2);
676
677 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
678 EXPECT_EQ(callouts[0]->priority(), 'H');
679
680 auto& fru1 = callouts[0]->fruIdentity();
681 EXPECT_EQ(fru1->getPN().value(), "1234567");
682 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
683 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
684
685 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
686 EXPECT_EQ(callouts[1]->priority(), 'M');
687
688 auto& fru2 = callouts[1]->fruIdentity();
689 EXPECT_EQ(fru2->getPN().value(), "2345678");
690 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
691 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
692 }
Matt Spinler03984582020-04-09 13:17:58 -0500693}
Matt Spinler717de422020-06-04 13:10:14 -0500694
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500695// Test that a symbolic FRU with a trusted location code callout
696// from the registry can get its location from the
697// CALLOUT_INVENTORY_PATH AdditionalData entry.
698TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
699{
700 message::Entry entry;
701 entry.src.type = 0xBD;
702 entry.src.reasonCode = 0xABCD;
703 entry.subsystem = 0x42;
704 entry.src.powerFault = false;
705
706 entry.callouts = R"(
707 [{
708 "CalloutList":
709 [
710 {
711 "Priority": "high",
712 "SymbolicFRUTrusted": "service_docs",
713 "UseInventoryLocCode": true
714 },
715 {
716 "Priority": "medium",
717 "LocCode": "P0-C8",
718 "SymbolicFRUTrusted": "pwrsply"
719 }
720 ]
721 }])"_json;
722
723 {
724 // The location code for the first symbolic FRU callout will
725 // come from this inventory path since UseInventoryLocCode is set.
726 // In this case there will be no normal FRU callout for the motherboard.
727 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
728 AdditionalData ad{adData};
729 NiceMock<MockDataInterface> dataIface;
730 std::vector<std::string> names{"systemA"};
731
Matt Spinler1ab66962020-10-29 13:21:44 -0500732 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500733
734 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
735 .Times(1)
736 .WillOnce(Return("Ufcs-P10"));
737
738 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
739 .WillOnce(Return("Ufcs-P0-C8"));
740
741 SRC src{entry, ad, dataIface};
742
743 auto& callouts = src.callouts()->callouts();
744 EXPECT_EQ(callouts.size(), 2);
745
746 // The location code for the first symbolic FRU callout with a
747 // trusted location code comes from the motherboard.
748 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
749 EXPECT_EQ(callouts[0]->priority(), 'H');
750 auto& fru1 = callouts[0]->fruIdentity();
751 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
752 EXPECT_EQ(fru1->failingComponentType(),
753 src::FRUIdentity::symbolicFRUTrustedLocCode);
754
755 // The second trusted symbolic FRU callouts uses the location
756 // code in the registry as usual.
757 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
758 EXPECT_EQ(callouts[1]->priority(), 'M');
759 auto& fru2 = callouts[1]->fruIdentity();
760 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
761 EXPECT_EQ(fru2->failingComponentType(),
762 src::FRUIdentity::symbolicFRUTrustedLocCode);
763 }
764
765 {
766 // This time say we want to use the location code from
767 // the inventory, but don't pass it in and the callout should
768 // end up a regular symbolic FRU
769 entry.callouts = R"(
770 [{
771 "CalloutList":
772 [
773 {
774 "Priority": "high",
775 "SymbolicFRUTrusted": "service_docs",
776 "UseInventoryLocCode": true
777 }
778 ]
779 }])"_json;
780
781 AdditionalData ad;
782 NiceMock<MockDataInterface> dataIface;
783 std::vector<std::string> names{"systemA"};
784
Matt Spinler1ab66962020-10-29 13:21:44 -0500785 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500786
787 SRC src{entry, ad, dataIface};
788
789 auto& callouts = src.callouts()->callouts();
790 EXPECT_EQ(callouts.size(), 1);
791
792 EXPECT_EQ(callouts[0]->locationCode(), "");
793 EXPECT_EQ(callouts[0]->priority(), 'H');
794 auto& fru1 = callouts[0]->fruIdentity();
795 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
796 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
797 }
798}
799
Matt Spinler717de422020-06-04 13:10:14 -0500800// Test looking up device path fails in the callout jSON.
801TEST_F(SRCTest, DevicePathCalloutTest)
802{
803 message::Entry entry;
804 entry.src.type = 0xBD;
805 entry.src.reasonCode = 0xABCD;
806 entry.subsystem = 0x42;
807 entry.src.powerFault = false;
808
809 const auto calloutJSON = R"(
810 {
811 "I2C":
812 {
813 "14":
814 {
815 "114":
816 {
817 "Callouts":[
818 {
819 "Name": "/chassis/motherboard/cpu0",
820 "LocationCode": "P1-C40",
821 "Priority": "H"
822 },
823 {
824 "Name": "/chassis/motherboard",
825 "LocationCode": "P1",
826 "Priority": "M"
827 },
828 {
829 "Name": "/chassis/motherboard/bmc",
830 "LocationCode": "P1-C15",
831 "Priority": "L"
832 }
833 ],
834 "Dest": "proc 0 target"
835 }
836 }
837 }
838 })";
839
840 auto dataPath = getPELReadOnlyDataPath();
841 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
842 file << calloutJSON;
843 file.close();
844
845 NiceMock<MockDataInterface> dataIface;
846 std::vector<std::string> names{"systemA"};
847
848 EXPECT_CALL(dataIface, getSystemNames)
849 .Times(5)
Matt Spinler1ab66962020-10-29 13:21:44 -0500850 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500851
Matt Spinler2f9225a2020-08-05 12:58:49 -0500852 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500853 .Times(3)
854 .WillRepeatedly(
855 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
856
Matt Spinler2f9225a2020-08-05 12:58:49 -0500857 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500858 .Times(3)
859 .WillRepeatedly(
860 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
861
Matt Spinler2f9225a2020-08-05 12:58:49 -0500862 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500863 .Times(3)
864 .WillRepeatedly(
865 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
866
867 EXPECT_CALL(dataIface,
868 getLocationCode(
869 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"))
870 .Times(3)
871 .WillRepeatedly(Return("Ufcs-P1-C40"));
872 EXPECT_CALL(
873 dataIface,
874 getLocationCode("/xyz/openbmc_project/inventory/chassis/motherboard"))
875 .Times(3)
876 .WillRepeatedly(Return("Ufcs-P1"));
877 EXPECT_CALL(dataIface,
878 getLocationCode(
879 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"))
880 .Times(3)
881 .WillRepeatedly(Return("Ufcs-P1-C15"));
882
883 EXPECT_CALL(
884 dataIface,
885 getHWCalloutFields(
886 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
887 .Times(3)
888 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
889 SetArgReferee<2>("CCCC"),
890 SetArgReferee<3>("123456789ABC")));
891 EXPECT_CALL(
892 dataIface,
893 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
894 _, _, _))
895 .Times(3)
896 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
897 SetArgReferee<2>("MMMM"),
898 SetArgReferee<3>("CBA987654321")));
899 EXPECT_CALL(
900 dataIface,
901 getHWCalloutFields(
902 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
903 .Times(3)
904 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
905 SetArgReferee<2>("BBBB"),
906 SetArgReferee<3>("C123456789AB")));
907
908 // Call this below with different AdditionalData values that
909 // result in the same callouts.
910 auto checkCallouts = [&entry, &dataIface](const auto& items) {
911 AdditionalData ad{items};
912 SRC src{entry, ad, dataIface};
913
914 ASSERT_TRUE(src.callouts());
915 auto& callouts = src.callouts()->callouts();
916
917 ASSERT_EQ(callouts.size(), 3);
918
919 {
920 EXPECT_EQ(callouts[0]->priority(), 'H');
921 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
922
923 auto& fru = callouts[0]->fruIdentity();
924 EXPECT_EQ(fru->getPN().value(), "1234567");
925 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
926 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
927 }
928 {
929 EXPECT_EQ(callouts[1]->priority(), 'M');
930 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
931
932 auto& fru = callouts[1]->fruIdentity();
933 EXPECT_EQ(fru->getPN().value(), "7654321");
934 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
935 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
936 }
937 {
938 EXPECT_EQ(callouts[2]->priority(), 'L');
939 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
940
941 auto& fru = callouts[2]->fruIdentity();
942 EXPECT_EQ(fru->getPN().value(), "7123456");
943 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
944 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
945 }
946 };
947
948 {
949 // Callouts based on the device path
950 std::vector<std::string> items{
951 "CALLOUT_ERRNO=5",
952 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
953 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
954
955 checkCallouts(items);
956 }
957
958 {
959 // Callouts based on the I2C bus and address
960 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
961 "CALLOUT_IIC_ADDR=0x72"};
962 checkCallouts(items);
963 }
964
965 {
966 // Also based on I2C bus and address, but with bus = /dev/i2c-14
967 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
968 "CALLOUT_IIC_ADDR=0x72"};
969 checkCallouts(items);
970 }
971
972 {
973 // Callout not found
974 std::vector<std::string> items{
975 "CALLOUT_ERRNO=5",
976 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
977 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
978
979 AdditionalData ad{items};
980 SRC src{entry, ad, dataIface};
981
982 EXPECT_FALSE(src.callouts());
983 ASSERT_EQ(src.getDebugData().size(), 1);
984 EXPECT_EQ(src.getDebugData()[0],
985 "Problem looking up I2C callouts on 24 18: "
986 "[json.exception.out_of_range.403] key '24' not found");
987 }
988
989 {
990 // Callout not found
991 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
992 "CALLOUT_IIC_ADDR=0x99"};
993 AdditionalData ad{items};
994 SRC src{entry, ad, dataIface};
995
996 EXPECT_FALSE(src.callouts());
997 ASSERT_EQ(src.getDebugData().size(), 1);
998 EXPECT_EQ(src.getDebugData()[0],
999 "Problem looking up I2C callouts on 22 153: "
1000 "[json.exception.out_of_range.403] key '22' not found");
1001 }
1002
1003 fs::remove_all(dataPath);
1004}
Matt Spinler3bdd0112020-08-27 10:24:34 -05001005
1006// Test when callouts are passed in via JSON
1007TEST_F(SRCTest, JsonCalloutsTest)
1008{
1009 const auto jsonCallouts = R"(
1010 [
1011 {
1012 "LocationCode": "P0-C1",
1013 "Priority": "H",
1014 "MRUs": [
1015 {
1016 "ID": 42,
1017 "Priority": "H"
1018 },
1019 {
1020 "ID": 43,
1021 "Priority": "M"
1022 }
1023 ]
1024 },
1025 {
1026 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1027 "Priority": "M",
1028 "Guarded": true,
1029 "Deconfigured": true
1030 },
1031 {
1032 "Procedure": "PROCEDU",
1033 "Priority": "A"
1034 },
1035 {
1036 "SymbolicFRU": "TRUSTED",
1037 "Priority": "B",
1038 "TrustedLocationCode": true,
1039 "LocationCode": "P1-C23"
1040 },
1041 {
1042 "SymbolicFRU": "FRUTST1",
1043 "Priority": "C",
1044 "LocationCode": "P1-C24"
1045 },
1046 {
1047 "SymbolicFRU": "FRUTST2LONG",
1048 "Priority": "L"
1049 }
1050 ]
1051 )"_json;
1052
1053 message::Entry entry;
1054 entry.src.type = 0xBD;
1055 entry.src.reasonCode = 0xABCD;
1056 entry.subsystem = 0x42;
1057 entry.src.powerFault = false;
1058
1059 AdditionalData ad;
1060 NiceMock<MockDataInterface> dataIface;
1061
1062 // Callout 0 mock calls
1063 {
1064 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1065 .Times(1)
1066 .WillOnce(Return("UXXX-P0-C1"));
1067 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1068 .Times(1)
1069 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1070 EXPECT_CALL(
1071 dataIface,
1072 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1073 .Times(1)
1074 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1075 SetArgReferee<2>("CCCC"),
1076 SetArgReferee<3>("123456789ABC")));
1077 }
1078 // Callout 1 mock calls
1079 {
1080 EXPECT_CALL(dataIface,
1081 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1082 .WillOnce(Return("UYYY-P5"));
1083 EXPECT_CALL(
1084 dataIface,
1085 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1086 .Times(1)
1087 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1088 SetArgReferee<2>("DDDD"),
1089 SetArgReferee<3>("23456789ABCD")));
1090 }
1091 // Callout 3 mock calls
1092 {
1093 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1094 .Times(1)
1095 .WillOnce(Return("UXXX-P1-C23"));
1096 }
1097 // Callout 4 mock calls
1098 {
1099 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1100 .Times(1)
1101 .WillOnce(Return("UXXX-P1-C24"));
1102 }
1103
1104 SRC src{entry, ad, jsonCallouts, dataIface};
1105 ASSERT_TRUE(src.callouts());
1106
Matt Spinlerafa2c792020-08-27 11:01:39 -05001107 // Check the guarded and deconfigured flags
1108 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1109
Matt Spinler3bdd0112020-08-27 10:24:34 -05001110 const auto& callouts = src.callouts()->callouts();
1111 ASSERT_EQ(callouts.size(), 6);
1112
1113 // Check callout 0
1114 {
1115 EXPECT_EQ(callouts[0]->priority(), 'H');
1116 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1117
1118 auto& fru = callouts[0]->fruIdentity();
1119 EXPECT_EQ(fru->getPN().value(), "1234567");
1120 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1121 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1122 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001123
1124 auto& mruCallouts = callouts[0]->mru();
1125 ASSERT_TRUE(mruCallouts);
1126 auto& mrus = mruCallouts->mrus();
1127 ASSERT_EQ(mrus.size(), 2);
1128 EXPECT_EQ(mrus[0].id, 42);
1129 EXPECT_EQ(mrus[0].priority, 'H');
1130 EXPECT_EQ(mrus[1].id, 43);
1131 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001132 }
1133
1134 // Check callout 1
1135 {
1136 EXPECT_EQ(callouts[1]->priority(), 'M');
1137 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1138
1139 auto& fru = callouts[1]->fruIdentity();
1140 EXPECT_EQ(fru->getPN().value(), "2345678");
1141 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1142 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1143 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1144 }
1145
1146 // Check callout 2
1147 {
1148 EXPECT_EQ(callouts[2]->priority(), 'A');
1149 EXPECT_EQ(callouts[2]->locationCode(), "");
1150
1151 auto& fru = callouts[2]->fruIdentity();
1152 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1153 EXPECT_EQ(fru->failingComponentType(),
1154 src::FRUIdentity::maintenanceProc);
1155 }
1156
1157 // Check callout 3
1158 {
1159 EXPECT_EQ(callouts[3]->priority(), 'B');
1160 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1161
1162 auto& fru = callouts[3]->fruIdentity();
1163 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1164 EXPECT_EQ(fru->failingComponentType(),
1165 src::FRUIdentity::symbolicFRUTrustedLocCode);
1166 }
1167
1168 // Check callout 4
1169 {
1170 EXPECT_EQ(callouts[4]->priority(), 'C');
1171 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1172
1173 auto& fru = callouts[4]->fruIdentity();
1174 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1175 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1176 }
1177
1178 // Check callout 5
1179 {
1180 EXPECT_EQ(callouts[5]->priority(), 'L');
1181 EXPECT_EQ(callouts[5]->locationCode(), "");
1182
1183 auto& fru = callouts[5]->fruIdentity();
1184 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1185 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1186 }
1187
1188 // Check that it didn't find any errors
1189 const auto& data = src.getDebugData();
1190 EXPECT_TRUE(data.empty());
1191}
1192
1193TEST_F(SRCTest, JsonBadCalloutsTest)
1194{
1195 // The first call will have a Throw in a mock call.
1196 // The second will have a different Throw in a mock call.
1197 // The others have issues with the Priority field.
1198 const auto jsonCallouts = R"(
1199 [
1200 {
1201 "LocationCode": "P0-C1",
1202 "Priority": "H"
1203 },
1204 {
1205 "LocationCode": "P0-C2",
1206 "Priority": "H"
1207 },
1208 {
1209 "LocationCode": "P0-C3"
1210 },
1211 {
1212 "LocationCode": "P0-C4",
1213 "Priority": "X"
1214 }
1215 ]
1216 )"_json;
1217
1218 message::Entry entry;
1219 entry.src.type = 0xBD;
1220 entry.src.reasonCode = 0xABCD;
1221 entry.subsystem = 0x42;
1222 entry.src.powerFault = false;
1223
1224 AdditionalData ad;
1225 NiceMock<MockDataInterface> dataIface;
1226
1227 // Callout 0 mock calls
1228 // Expand location code will fail, so the unexpanded location
1229 // code should show up in the callout instead.
1230 {
1231 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1232 .WillOnce(Throw(std::runtime_error("Fail")));
1233
1234 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1235 .Times(1)
1236 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1237 EXPECT_CALL(
1238 dataIface,
1239 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1240 .Times(1)
1241 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1242 SetArgReferee<2>("CCCC"),
1243 SetArgReferee<3>("123456789ABC")));
1244 }
1245
1246 // Callout 1 mock calls
1247 // getInventoryFromLocCode will fail
1248 {
1249 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1250 .Times(1)
1251 .WillOnce(Return("UXXX-P0-C2"));
1252
1253 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1254 .Times(1)
1255 .WillOnce(Throw(std::runtime_error("Fail")));
1256 }
1257
1258 SRC src{entry, ad, jsonCallouts, dataIface};
1259
1260 ASSERT_TRUE(src.callouts());
1261
1262 const auto& callouts = src.callouts()->callouts();
1263
1264 // Only the first callout was successful
1265 ASSERT_EQ(callouts.size(), 1);
1266
1267 {
1268 EXPECT_EQ(callouts[0]->priority(), 'H');
1269 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1270
1271 auto& fru = callouts[0]->fruIdentity();
1272 EXPECT_EQ(fru->getPN().value(), "1234567");
1273 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1274 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1275 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1276 }
1277
1278 const auto& data = src.getDebugData();
1279 ASSERT_EQ(data.size(), 4);
1280 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1281 EXPECT_STREQ(data[1].c_str(),
1282 "Failed extracting callout data from JSON: Unable to "
1283 "get inventory path from location code: P0-C2: Fail");
1284 EXPECT_STREQ(data[2].c_str(),
1285 "Failed extracting callout data from JSON: "
1286 "[json.exception.out_of_range.403] key 'Priority' not found");
1287 EXPECT_STREQ(data[3].c_str(),
1288 "Failed extracting callout data from JSON: Invalid "
1289 "priority 'X' found in JSON callout");
1290}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001291
1292// Test that an inventory path callout can have
1293// a different priority than H.
1294TEST_F(SRCTest, InventoryCalloutTestPriority)
1295{
1296 message::Entry entry;
1297 entry.src.type = 0xBD;
1298 entry.src.reasonCode = 0xABCD;
1299 entry.subsystem = 0x42;
1300 entry.src.powerFault = false;
1301
1302 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1303 "CALLOUT_PRIORITY=M"};
1304 AdditionalData ad{adData};
1305 NiceMock<MockDataInterface> dataIface;
1306
1307 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1308 .WillOnce(Return("UTMS-P1"));
1309
1310 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1311 .Times(1)
1312 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1313 SetArgReferee<3>("123456789ABC")));
1314
1315 SRC src{entry, ad, dataIface};
1316 EXPECT_TRUE(src.valid());
1317
1318 ASSERT_TRUE(src.callouts());
1319
1320 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1321
1322 auto& callout = src.callouts()->callouts().front();
1323
1324 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1325 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001326}