blob: 301faac1b6f110fd7f5c5024c76df4c5021e0c9a [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
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// Test that an inventory path callout string is
320// converted into the appropriate FRU callout.
321TEST_F(SRCTest, InventoryCalloutTest)
322{
323 message::Entry entry;
324 entry.src.type = 0xBD;
325 entry.src.reasonCode = 0xABCD;
326 entry.subsystem = 0x42;
327 entry.src.powerFault = false;
328
329 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
330 AdditionalData ad{adData};
331 NiceMock<MockDataInterface> dataIface;
332
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500333 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
334 .WillOnce(Return("UTMS-P1"));
335
336 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500337 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500338 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
339 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500340
341 SRC src{entry, ad, dataIface};
342 EXPECT_TRUE(src.valid());
343
344 ASSERT_TRUE(src.callouts());
345
346 EXPECT_EQ(src.callouts()->callouts().size(), 1);
347
348 auto& callout = src.callouts()->callouts().front();
349
350 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500351 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500352
353 auto& fru = callout->fruIdentity();
354
355 EXPECT_EQ(fru->getPN().value(), "1234567");
356 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
357 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
358
359 // flatten and unflatten
360 std::vector<uint8_t> data;
361 Stream stream{data};
362 src.flatten(stream);
363
364 stream.offset(0);
365 SRC newSRC{stream};
366 EXPECT_TRUE(newSRC.valid());
367 ASSERT_TRUE(src.callouts());
368 EXPECT_EQ(src.callouts()->callouts().size(), 1);
369}
370
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500371// Test that when the location code can't be obtained that
Matt Spinlered046852020-03-13 13:58:15 -0500372// a procedure callout is used.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500373TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
374{
375 message::Entry entry;
376 entry.src.type = 0xBD;
377 entry.src.reasonCode = 0xABCD;
378 entry.subsystem = 0x42;
379 entry.src.powerFault = false;
380
381 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
382 AdditionalData ad{adData};
383 NiceMock<MockDataInterface> dataIface;
384
385 auto func = []() {
386 throw sdbusplus::exception::SdBusError(5, "Error");
387 return std::string{};
388 };
389
390 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
391 .Times(1)
392 .WillOnce(InvokeWithoutArgs(func));
393
394 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
395
396 SRC src{entry, ad, dataIface};
397 EXPECT_TRUE(src.valid());
398
399 ASSERT_TRUE(src.callouts());
400
401 EXPECT_EQ(src.callouts()->callouts().size(), 1);
402
403 auto& callout = src.callouts()->callouts().front();
404 EXPECT_EQ(callout->locationCodeSize(), 0);
Matt Spinler717de422020-06-04 13:10:14 -0500405 EXPECT_EQ(callout->priority(), 'H');
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500406
407 auto& fru = callout->fruIdentity();
408
409 EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01");
410 EXPECT_FALSE(fru->getPN());
411 EXPECT_FALSE(fru->getSN());
412 EXPECT_FALSE(fru->getCCIN());
413
414 // flatten and unflatten
415 std::vector<uint8_t> data;
416 Stream stream{data};
417 src.flatten(stream);
418
419 stream.offset(0);
420 SRC newSRC{stream};
421 EXPECT_TRUE(newSRC.valid());
422 ASSERT_TRUE(src.callouts());
423 EXPECT_EQ(src.callouts()->callouts().size(), 1);
424}
425
426// Test that when the VPD can't be obtained that
427// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500428TEST_F(SRCTest, InventoryCalloutNoVPDTest)
429{
430 message::Entry entry;
431 entry.src.type = 0xBD;
432 entry.src.reasonCode = 0xABCD;
433 entry.subsystem = 0x42;
434 entry.src.powerFault = false;
435
436 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
437 AdditionalData ad{adData};
438 NiceMock<MockDataInterface> dataIface;
439
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500440 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
441 .Times(1)
442 .WillOnce(Return("UTMS-P10"));
443
Matt Spinlered046852020-03-13 13:58:15 -0500444 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
445
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500446 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500447 .Times(1)
448 .WillOnce(InvokeWithoutArgs(func));
449
450 SRC src{entry, ad, dataIface};
451 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500452 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500453 EXPECT_EQ(src.callouts()->callouts().size(), 1);
454
455 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500456 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500457 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500458
459 auto& fru = callout->fruIdentity();
460
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500461 EXPECT_EQ(fru->getPN(), "");
462 EXPECT_EQ(fru->getCCIN(), "");
463 EXPECT_EQ(fru->getSN(), "");
464 EXPECT_FALSE(fru->getMaintProc());
465
Matt Spinlered046852020-03-13 13:58:15 -0500466 // flatten and unflatten
467 std::vector<uint8_t> data;
468 Stream stream{data};
469 src.flatten(stream);
470
471 stream.offset(0);
472 SRC newSRC{stream};
473 EXPECT_TRUE(newSRC.valid());
474 ASSERT_TRUE(src.callouts());
475 EXPECT_EQ(src.callouts()->callouts().size(), 1);
476}
Matt Spinler03984582020-04-09 13:17:58 -0500477
478TEST_F(SRCTest, RegistryCalloutTest)
479{
480 message::Entry entry;
481 entry.src.type = 0xBD;
482 entry.src.reasonCode = 0xABCD;
483 entry.subsystem = 0x42;
484 entry.src.powerFault = false;
485
486 entry.callouts = R"(
487 [
488 {
489 "System": "systemA",
490 "CalloutList":
491 [
492 {
493 "Priority": "high",
494 "SymbolicFRU": "service_docs"
495 },
496 {
497 "Priority": "medium",
498 "Procedure": "no_vpd_for_fru"
499 }
500 ]
501 },
502 {
503 "System": "systemB",
504 "CalloutList":
505 [
506 {
507 "Priority": "high",
508 "LocCode": "P0-C8",
509 "SymbolicFRUTrusted": "service_docs"
510 },
511 {
512 "Priority": "medium",
513 "SymbolicFRUTrusted": "service_docs"
514 }
515 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500516 },
517 {
518 "System": "systemC",
519 "CalloutList":
520 [
521 {
522 "Priority": "high",
523 "LocCode": "P0-C8"
524 },
525 {
526 "Priority": "medium",
527 "LocCode": "P0-C9"
528 }
529 ]
Matt Spinler03984582020-04-09 13:17:58 -0500530 }
531 ])"_json;
532
533 {
534 // Call out a symbolic FRU and a procedure
535 AdditionalData ad;
536 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500537 std::vector<std::string> names{"systemA"};
538
Matt Spinler1ab66962020-10-29 13:21:44 -0500539 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500540
541 SRC src{entry, ad, dataIface};
542
543 auto& callouts = src.callouts()->callouts();
544 ASSERT_EQ(callouts.size(), 2);
545
546 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
547 EXPECT_EQ(callouts[0]->priority(), 'H');
548
549 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
550 EXPECT_EQ(callouts[1]->priority(), 'M');
551
552 auto& fru1 = callouts[0]->fruIdentity();
553 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
554 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
555 EXPECT_FALSE(fru1->getMaintProc());
556 EXPECT_FALSE(fru1->getSN());
557 EXPECT_FALSE(fru1->getCCIN());
558
559 auto& fru2 = callouts[1]->fruIdentity();
560 EXPECT_EQ(fru2->getMaintProc().value(), "BMCSP01");
561 EXPECT_EQ(fru2->failingComponentType(),
562 src::FRUIdentity::maintenanceProc);
563 EXPECT_FALSE(fru2->getPN());
564 EXPECT_FALSE(fru2->getSN());
565 EXPECT_FALSE(fru2->getCCIN());
566 }
567
568 {
569 // Call out a trusted symbolic FRU with a location code, and
570 // another one without.
571 AdditionalData ad;
572 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500573 std::vector<std::string> names{"systemB"};
574
Matt Spinleraf191c72020-06-04 11:35:13 -0500575 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500576 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500577
578 SRC src{entry, ad, dataIface};
579
580 auto& callouts = src.callouts()->callouts();
581 EXPECT_EQ(callouts.size(), 2);
582
583 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
584 EXPECT_EQ(callouts[0]->priority(), 'H');
585
586 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
587 EXPECT_EQ(callouts[1]->priority(), 'M');
588
589 auto& fru1 = callouts[0]->fruIdentity();
590 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
591 EXPECT_EQ(fru1->failingComponentType(),
592 src::FRUIdentity::symbolicFRUTrustedLocCode);
593 EXPECT_FALSE(fru1->getMaintProc());
594 EXPECT_FALSE(fru1->getSN());
595 EXPECT_FALSE(fru1->getCCIN());
596
597 // It asked for a trusted symbolic FRU, but no location code
598 // was provided so it is switched back to a normal one
599 auto& fru2 = callouts[1]->fruIdentity();
600 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
601 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
602 EXPECT_FALSE(fru2->getMaintProc());
603 EXPECT_FALSE(fru2->getSN());
604 EXPECT_FALSE(fru2->getCCIN());
605 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500606
607 {
608 // Two hardware callouts
609 AdditionalData ad;
610 NiceMock<MockDataInterface> dataIface;
611 std::vector<std::string> names{"systemC"};
612
Matt Spinler1ab66962020-10-29 13:21:44 -0500613 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500614
615 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
616 .WillOnce(Return("UXXX-P0-C8"));
617
618 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
619 .WillOnce(Return("UXXX-P0-C9"));
620
Matt Spinler2f9225a2020-08-05 12:58:49 -0500621 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500622 .WillOnce(Return(
623 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
624
Matt Spinler2f9225a2020-08-05 12:58:49 -0500625 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500626 .WillOnce(Return(
627 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
628
629 EXPECT_CALL(
630 dataIface,
631 getHWCalloutFields(
632 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
633 _))
634 .Times(1)
635 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
636 SetArgReferee<2>("CCCC"),
637 SetArgReferee<3>("123456789ABC")));
638
639 EXPECT_CALL(
640 dataIface,
641 getHWCalloutFields(
642 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
643 _))
644 .Times(1)
645 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
646 SetArgReferee<2>("DDDD"),
647 SetArgReferee<3>("23456789ABCD")));
648
649 SRC src{entry, ad, dataIface};
650
651 auto& callouts = src.callouts()->callouts();
652 EXPECT_EQ(callouts.size(), 2);
653
654 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
655 EXPECT_EQ(callouts[0]->priority(), 'H');
656
657 auto& fru1 = callouts[0]->fruIdentity();
658 EXPECT_EQ(fru1->getPN().value(), "1234567");
659 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
660 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
661
662 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
663 EXPECT_EQ(callouts[1]->priority(), 'M');
664
665 auto& fru2 = callouts[1]->fruIdentity();
666 EXPECT_EQ(fru2->getPN().value(), "2345678");
667 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
668 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
669 }
Matt Spinler03984582020-04-09 13:17:58 -0500670}
Matt Spinler717de422020-06-04 13:10:14 -0500671
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500672// Test that a symbolic FRU with a trusted location code callout
673// from the registry can get its location from the
674// CALLOUT_INVENTORY_PATH AdditionalData entry.
675TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
676{
677 message::Entry entry;
678 entry.src.type = 0xBD;
679 entry.src.reasonCode = 0xABCD;
680 entry.subsystem = 0x42;
681 entry.src.powerFault = false;
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::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
705 AdditionalData ad{adData};
706 NiceMock<MockDataInterface> dataIface;
707 std::vector<std::string> names{"systemA"};
708
Matt Spinler1ab66962020-10-29 13:21:44 -0500709 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500710
711 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
712 .Times(1)
713 .WillOnce(Return("Ufcs-P10"));
714
715 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
716 .WillOnce(Return("Ufcs-P0-C8"));
717
718 SRC src{entry, ad, dataIface};
719
720 auto& callouts = src.callouts()->callouts();
721 EXPECT_EQ(callouts.size(), 2);
722
723 // The location code for the first symbolic FRU callout with a
724 // trusted location code comes from the motherboard.
725 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
726 EXPECT_EQ(callouts[0]->priority(), 'H');
727 auto& fru1 = callouts[0]->fruIdentity();
728 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
729 EXPECT_EQ(fru1->failingComponentType(),
730 src::FRUIdentity::symbolicFRUTrustedLocCode);
731
732 // The second trusted symbolic FRU callouts uses the location
733 // code in the registry as usual.
734 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
735 EXPECT_EQ(callouts[1]->priority(), 'M');
736 auto& fru2 = callouts[1]->fruIdentity();
737 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
738 EXPECT_EQ(fru2->failingComponentType(),
739 src::FRUIdentity::symbolicFRUTrustedLocCode);
740 }
741
742 {
743 // This time say we want to use the location code from
744 // the inventory, but don't pass it in and the callout should
745 // end up a regular symbolic FRU
746 entry.callouts = R"(
747 [{
748 "CalloutList":
749 [
750 {
751 "Priority": "high",
752 "SymbolicFRUTrusted": "service_docs",
753 "UseInventoryLocCode": true
754 }
755 ]
756 }])"_json;
757
758 AdditionalData ad;
759 NiceMock<MockDataInterface> dataIface;
760 std::vector<std::string> names{"systemA"};
761
Matt Spinler1ab66962020-10-29 13:21:44 -0500762 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500763
764 SRC src{entry, ad, dataIface};
765
766 auto& callouts = src.callouts()->callouts();
767 EXPECT_EQ(callouts.size(), 1);
768
769 EXPECT_EQ(callouts[0]->locationCode(), "");
770 EXPECT_EQ(callouts[0]->priority(), 'H');
771 auto& fru1 = callouts[0]->fruIdentity();
772 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
773 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
774 }
775}
776
Matt Spinler717de422020-06-04 13:10:14 -0500777// Test looking up device path fails in the callout jSON.
778TEST_F(SRCTest, DevicePathCalloutTest)
779{
780 message::Entry entry;
781 entry.src.type = 0xBD;
782 entry.src.reasonCode = 0xABCD;
783 entry.subsystem = 0x42;
784 entry.src.powerFault = false;
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)
Matt Spinler1ab66962020-10-29 13:21:44 -0500827 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500828
Matt Spinler2f9225a2020-08-05 12:58:49 -0500829 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500830 .Times(3)
831 .WillRepeatedly(
832 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
833
Matt Spinler2f9225a2020-08-05 12:58:49 -0500834 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500835 .Times(3)
836 .WillRepeatedly(
837 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
838
Matt Spinler2f9225a2020-08-05 12:58:49 -0500839 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500840 .Times(3)
841 .WillRepeatedly(
842 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
843
844 EXPECT_CALL(dataIface,
845 getLocationCode(
846 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"))
847 .Times(3)
848 .WillRepeatedly(Return("Ufcs-P1-C40"));
849 EXPECT_CALL(
850 dataIface,
851 getLocationCode("/xyz/openbmc_project/inventory/chassis/motherboard"))
852 .Times(3)
853 .WillRepeatedly(Return("Ufcs-P1"));
854 EXPECT_CALL(dataIface,
855 getLocationCode(
856 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"))
857 .Times(3)
858 .WillRepeatedly(Return("Ufcs-P1-C15"));
859
860 EXPECT_CALL(
861 dataIface,
862 getHWCalloutFields(
863 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
864 .Times(3)
865 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
866 SetArgReferee<2>("CCCC"),
867 SetArgReferee<3>("123456789ABC")));
868 EXPECT_CALL(
869 dataIface,
870 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
871 _, _, _))
872 .Times(3)
873 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
874 SetArgReferee<2>("MMMM"),
875 SetArgReferee<3>("CBA987654321")));
876 EXPECT_CALL(
877 dataIface,
878 getHWCalloutFields(
879 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
880 .Times(3)
881 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
882 SetArgReferee<2>("BBBB"),
883 SetArgReferee<3>("C123456789AB")));
884
885 // Call this below with different AdditionalData values that
886 // result in the same callouts.
887 auto checkCallouts = [&entry, &dataIface](const auto& items) {
888 AdditionalData ad{items};
889 SRC src{entry, ad, dataIface};
890
891 ASSERT_TRUE(src.callouts());
892 auto& callouts = src.callouts()->callouts();
893
894 ASSERT_EQ(callouts.size(), 3);
895
896 {
897 EXPECT_EQ(callouts[0]->priority(), 'H');
898 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
899
900 auto& fru = callouts[0]->fruIdentity();
901 EXPECT_EQ(fru->getPN().value(), "1234567");
902 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
903 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
904 }
905 {
906 EXPECT_EQ(callouts[1]->priority(), 'M');
907 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
908
909 auto& fru = callouts[1]->fruIdentity();
910 EXPECT_EQ(fru->getPN().value(), "7654321");
911 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
912 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
913 }
914 {
915 EXPECT_EQ(callouts[2]->priority(), 'L');
916 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
917
918 auto& fru = callouts[2]->fruIdentity();
919 EXPECT_EQ(fru->getPN().value(), "7123456");
920 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
921 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
922 }
923 };
924
925 {
926 // Callouts based on the device path
927 std::vector<std::string> items{
928 "CALLOUT_ERRNO=5",
929 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
930 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
931
932 checkCallouts(items);
933 }
934
935 {
936 // Callouts based on the I2C bus and address
937 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
938 "CALLOUT_IIC_ADDR=0x72"};
939 checkCallouts(items);
940 }
941
942 {
943 // Also based on I2C bus and address, but with bus = /dev/i2c-14
944 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
945 "CALLOUT_IIC_ADDR=0x72"};
946 checkCallouts(items);
947 }
948
949 {
950 // Callout not found
951 std::vector<std::string> items{
952 "CALLOUT_ERRNO=5",
953 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
954 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
955
956 AdditionalData ad{items};
957 SRC src{entry, ad, dataIface};
958
959 EXPECT_FALSE(src.callouts());
960 ASSERT_EQ(src.getDebugData().size(), 1);
961 EXPECT_EQ(src.getDebugData()[0],
962 "Problem looking up I2C callouts on 24 18: "
963 "[json.exception.out_of_range.403] key '24' not found");
964 }
965
966 {
967 // Callout not found
968 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
969 "CALLOUT_IIC_ADDR=0x99"};
970 AdditionalData ad{items};
971 SRC src{entry, ad, dataIface};
972
973 EXPECT_FALSE(src.callouts());
974 ASSERT_EQ(src.getDebugData().size(), 1);
975 EXPECT_EQ(src.getDebugData()[0],
976 "Problem looking up I2C callouts on 22 153: "
977 "[json.exception.out_of_range.403] key '22' not found");
978 }
979
980 fs::remove_all(dataPath);
981}
Matt Spinler3bdd0112020-08-27 10:24:34 -0500982
983// Test when callouts are passed in via JSON
984TEST_F(SRCTest, JsonCalloutsTest)
985{
986 const auto jsonCallouts = R"(
987 [
988 {
989 "LocationCode": "P0-C1",
990 "Priority": "H",
991 "MRUs": [
992 {
993 "ID": 42,
994 "Priority": "H"
995 },
996 {
997 "ID": 43,
998 "Priority": "M"
999 }
1000 ]
1001 },
1002 {
1003 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1004 "Priority": "M",
1005 "Guarded": true,
1006 "Deconfigured": true
1007 },
1008 {
1009 "Procedure": "PROCEDU",
1010 "Priority": "A"
1011 },
1012 {
1013 "SymbolicFRU": "TRUSTED",
1014 "Priority": "B",
1015 "TrustedLocationCode": true,
1016 "LocationCode": "P1-C23"
1017 },
1018 {
1019 "SymbolicFRU": "FRUTST1",
1020 "Priority": "C",
1021 "LocationCode": "P1-C24"
1022 },
1023 {
1024 "SymbolicFRU": "FRUTST2LONG",
1025 "Priority": "L"
1026 }
1027 ]
1028 )"_json;
1029
1030 message::Entry entry;
1031 entry.src.type = 0xBD;
1032 entry.src.reasonCode = 0xABCD;
1033 entry.subsystem = 0x42;
1034 entry.src.powerFault = false;
1035
1036 AdditionalData ad;
1037 NiceMock<MockDataInterface> dataIface;
1038
1039 // Callout 0 mock calls
1040 {
1041 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1042 .Times(1)
1043 .WillOnce(Return("UXXX-P0-C1"));
1044 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1045 .Times(1)
1046 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1047 EXPECT_CALL(
1048 dataIface,
1049 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1050 .Times(1)
1051 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1052 SetArgReferee<2>("CCCC"),
1053 SetArgReferee<3>("123456789ABC")));
1054 }
1055 // Callout 1 mock calls
1056 {
1057 EXPECT_CALL(dataIface,
1058 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1059 .WillOnce(Return("UYYY-P5"));
1060 EXPECT_CALL(
1061 dataIface,
1062 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1063 .Times(1)
1064 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1065 SetArgReferee<2>("DDDD"),
1066 SetArgReferee<3>("23456789ABCD")));
1067 }
1068 // Callout 3 mock calls
1069 {
1070 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1071 .Times(1)
1072 .WillOnce(Return("UXXX-P1-C23"));
1073 }
1074 // Callout 4 mock calls
1075 {
1076 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1077 .Times(1)
1078 .WillOnce(Return("UXXX-P1-C24"));
1079 }
1080
1081 SRC src{entry, ad, jsonCallouts, dataIface};
1082 ASSERT_TRUE(src.callouts());
1083
Matt Spinlerafa2c792020-08-27 11:01:39 -05001084 // Check the guarded and deconfigured flags
1085 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1086
Matt Spinler3bdd0112020-08-27 10:24:34 -05001087 const auto& callouts = src.callouts()->callouts();
1088 ASSERT_EQ(callouts.size(), 6);
1089
1090 // Check callout 0
1091 {
1092 EXPECT_EQ(callouts[0]->priority(), 'H');
1093 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1094
1095 auto& fru = callouts[0]->fruIdentity();
1096 EXPECT_EQ(fru->getPN().value(), "1234567");
1097 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1098 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1099 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001100
1101 auto& mruCallouts = callouts[0]->mru();
1102 ASSERT_TRUE(mruCallouts);
1103 auto& mrus = mruCallouts->mrus();
1104 ASSERT_EQ(mrus.size(), 2);
1105 EXPECT_EQ(mrus[0].id, 42);
1106 EXPECT_EQ(mrus[0].priority, 'H');
1107 EXPECT_EQ(mrus[1].id, 43);
1108 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001109 }
1110
1111 // Check callout 1
1112 {
1113 EXPECT_EQ(callouts[1]->priority(), 'M');
1114 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1115
1116 auto& fru = callouts[1]->fruIdentity();
1117 EXPECT_EQ(fru->getPN().value(), "2345678");
1118 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1119 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1120 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1121 }
1122
1123 // Check callout 2
1124 {
1125 EXPECT_EQ(callouts[2]->priority(), 'A');
1126 EXPECT_EQ(callouts[2]->locationCode(), "");
1127
1128 auto& fru = callouts[2]->fruIdentity();
1129 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1130 EXPECT_EQ(fru->failingComponentType(),
1131 src::FRUIdentity::maintenanceProc);
1132 }
1133
1134 // Check callout 3
1135 {
1136 EXPECT_EQ(callouts[3]->priority(), 'B');
1137 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1138
1139 auto& fru = callouts[3]->fruIdentity();
1140 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1141 EXPECT_EQ(fru->failingComponentType(),
1142 src::FRUIdentity::symbolicFRUTrustedLocCode);
1143 }
1144
1145 // Check callout 4
1146 {
1147 EXPECT_EQ(callouts[4]->priority(), 'C');
1148 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1149
1150 auto& fru = callouts[4]->fruIdentity();
1151 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1152 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1153 }
1154
1155 // Check callout 5
1156 {
1157 EXPECT_EQ(callouts[5]->priority(), 'L');
1158 EXPECT_EQ(callouts[5]->locationCode(), "");
1159
1160 auto& fru = callouts[5]->fruIdentity();
1161 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1162 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1163 }
1164
1165 // Check that it didn't find any errors
1166 const auto& data = src.getDebugData();
1167 EXPECT_TRUE(data.empty());
1168}
1169
1170TEST_F(SRCTest, JsonBadCalloutsTest)
1171{
1172 // The first call will have a Throw in a mock call.
1173 // The second will have a different Throw in a mock call.
1174 // The others have issues with the Priority field.
1175 const auto jsonCallouts = R"(
1176 [
1177 {
1178 "LocationCode": "P0-C1",
1179 "Priority": "H"
1180 },
1181 {
1182 "LocationCode": "P0-C2",
1183 "Priority": "H"
1184 },
1185 {
1186 "LocationCode": "P0-C3"
1187 },
1188 {
1189 "LocationCode": "P0-C4",
1190 "Priority": "X"
1191 }
1192 ]
1193 )"_json;
1194
1195 message::Entry entry;
1196 entry.src.type = 0xBD;
1197 entry.src.reasonCode = 0xABCD;
1198 entry.subsystem = 0x42;
1199 entry.src.powerFault = false;
1200
1201 AdditionalData ad;
1202 NiceMock<MockDataInterface> dataIface;
1203
1204 // Callout 0 mock calls
1205 // Expand location code will fail, so the unexpanded location
1206 // code should show up in the callout instead.
1207 {
1208 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1209 .WillOnce(Throw(std::runtime_error("Fail")));
1210
1211 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1212 .Times(1)
1213 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1214 EXPECT_CALL(
1215 dataIface,
1216 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1217 .Times(1)
1218 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1219 SetArgReferee<2>("CCCC"),
1220 SetArgReferee<3>("123456789ABC")));
1221 }
1222
1223 // Callout 1 mock calls
1224 // getInventoryFromLocCode will fail
1225 {
1226 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1227 .Times(1)
1228 .WillOnce(Return("UXXX-P0-C2"));
1229
1230 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1231 .Times(1)
1232 .WillOnce(Throw(std::runtime_error("Fail")));
1233 }
1234
1235 SRC src{entry, ad, jsonCallouts, dataIface};
1236
1237 ASSERT_TRUE(src.callouts());
1238
1239 const auto& callouts = src.callouts()->callouts();
1240
1241 // Only the first callout was successful
1242 ASSERT_EQ(callouts.size(), 1);
1243
1244 {
1245 EXPECT_EQ(callouts[0]->priority(), 'H');
1246 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1247
1248 auto& fru = callouts[0]->fruIdentity();
1249 EXPECT_EQ(fru->getPN().value(), "1234567");
1250 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1251 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1252 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1253 }
1254
1255 const auto& data = src.getDebugData();
1256 ASSERT_EQ(data.size(), 4);
1257 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1258 EXPECT_STREQ(data[1].c_str(),
1259 "Failed extracting callout data from JSON: Unable to "
1260 "get inventory path from location code: P0-C2: Fail");
1261 EXPECT_STREQ(data[2].c_str(),
1262 "Failed extracting callout data from JSON: "
1263 "[json.exception.out_of_range.403] key 'Priority' not found");
1264 EXPECT_STREQ(data[3].c_str(),
1265 "Failed extracting callout data from JSON: Invalid "
1266 "priority 'X' found in JSON callout");
1267}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001268
1269// Test that an inventory path callout can have
1270// a different priority than H.
1271TEST_F(SRCTest, InventoryCalloutTestPriority)
1272{
1273 message::Entry entry;
1274 entry.src.type = 0xBD;
1275 entry.src.reasonCode = 0xABCD;
1276 entry.subsystem = 0x42;
1277 entry.src.powerFault = false;
1278
1279 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1280 "CALLOUT_PRIORITY=M"};
1281 AdditionalData ad{adData};
1282 NiceMock<MockDataInterface> dataIface;
1283
1284 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1285 .WillOnce(Return("UTMS-P1"));
1286
1287 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1288 .Times(1)
1289 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1290 SetArgReferee<3>("123456789ABC")));
1291
1292 SRC src{entry, ad, dataIface};
1293 EXPECT_TRUE(src.valid());
1294
1295 ASSERT_TRUE(src.callouts());
1296
1297 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1298
1299 auto& callout = src.callouts()->callouts().front();
1300
1301 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1302 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001303}