blob: e3179975e344029fded37cedbdea155483d4b050 [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 Spinlerb8323632019-09-20 15:11:04 -050016#include "elog_entry.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050017#include "extensions/openpower-pels/generic.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050018#include "extensions/openpower-pels/pel.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050019#include "mocks.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050020#include "pel_utils.hpp"
21
22#include <filesystem>
23#include <fstream>
24
25#include <gtest/gtest.h>
26
27namespace fs = std::filesystem;
28using namespace openpower::pels;
Matt Spinler0a90a852020-06-04 13:18:27 -050029using ::testing::_;
William A. Kennington IIIb41fa542021-05-29 14:45:16 -070030using ::testing::DoAll;
Matt Spinler56ad2a02020-03-26 14:00:52 -050031using ::testing::NiceMock;
Matt Spinler677381b2020-01-23 10:04:29 -060032using ::testing::Return;
Matt Spinler0a90a852020-06-04 13:18:27 -050033using ::testing::SetArgReferee;
Matt Spinlercb6b0592019-07-16 15:58:51 -050034
35class PELTest : public CleanLogID
Patrick Williams2544b412022-10-04 08:41:06 -050036{};
Matt Spinlercb6b0592019-07-16 15:58:51 -050037
Matt Spinler5b289b22020-03-26 14:27:19 -050038fs::path makeTempDir()
39{
40 char path[] = "/tmp/tempdirXXXXXX";
41 std::filesystem::path dir = mkdtemp(path);
42 return dir;
43}
44
45int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data)
46{
47 static size_t count = 0;
48 fs::path path = dir / (std::string{"file"} + std::to_string(count));
49 std::ofstream stream{path};
50 count++;
51
52 stream.write(reinterpret_cast<const char*>(data.data()), data.size());
53 stream.close();
54
55 FILE* fp = fopen(path.c_str(), "r");
56 return fileno(fp);
57}
58
Matt Spinlercb6b0592019-07-16 15:58:51 -050059TEST_F(PELTest, FlattenTest)
60{
Matt Spinler42828bd2019-10-11 10:39:30 -050061 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler42828bd2019-10-11 10:39:30 -050062 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050063
64 // Check a few fields
65 EXPECT_TRUE(pel->valid());
66 EXPECT_EQ(pel->id(), 0x80818283);
67 EXPECT_EQ(pel->plid(), 0x50515253);
Matt Spinler97d19b42019-10-29 11:34:03 -050068 EXPECT_EQ(pel->userHeader().subsystem(), 0x10);
69 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0);
Matt Spinlercb6b0592019-07-16 15:58:51 -050070
71 // Test that data in == data out
72 auto flattenedData = pel->data();
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060073 EXPECT_EQ(data, flattenedData);
74 EXPECT_EQ(flattenedData.size(), pel->size());
Matt Spinlercb6b0592019-07-16 15:58:51 -050075}
76
77TEST_F(PELTest, CommitTimeTest)
78{
Matt Spinler42828bd2019-10-11 10:39:30 -050079 auto data = pelDataFactory(TestPELType::pelSimple);
80 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050081
82 auto origTime = pel->commitTime();
83 pel->setCommitTime();
84 auto newTime = pel->commitTime();
85
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060086 EXPECT_NE(origTime, newTime);
Matt Spinlercb6b0592019-07-16 15:58:51 -050087
88 // Make a new PEL and check new value is still there
89 auto newData = pel->data();
90 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060091 EXPECT_EQ(newTime, newPel->commitTime());
Matt Spinlercb6b0592019-07-16 15:58:51 -050092}
93
94TEST_F(PELTest, AssignIDTest)
95{
Matt Spinler42828bd2019-10-11 10:39:30 -050096 auto data = pelDataFactory(TestPELType::pelSimple);
97 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050098
99 auto origID = pel->id();
100 pel->assignID();
101 auto newID = pel->id();
102
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600103 EXPECT_NE(origID, newID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500104
105 // Make a new PEL and check new value is still there
106 auto newData = pel->data();
107 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600108 EXPECT_EQ(newID, newPel->id());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500109}
110
111TEST_F(PELTest, WithLogIDTest)
112{
Matt Spinler42828bd2019-10-11 10:39:30 -0500113 auto data = pelDataFactory(TestPELType::pelSimple);
114 auto pel = std::make_unique<PEL>(data, 0x42);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500115
116 EXPECT_TRUE(pel->valid());
117 EXPECT_EQ(pel->obmcLogID(), 0x42);
118}
119
120TEST_F(PELTest, InvalidPELTest)
121{
Matt Spinler42828bd2019-10-11 10:39:30 -0500122 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500123
124 // Too small
Matt Spinler42828bd2019-10-11 10:39:30 -0500125 data.resize(PrivateHeader::flattenedSize());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500126
Matt Spinler42828bd2019-10-11 10:39:30 -0500127 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500128
Matt Spinler97d19b42019-10-29 11:34:03 -0500129 EXPECT_TRUE(pel->privateHeader().valid());
130 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500131 EXPECT_FALSE(pel->valid());
132
Matt Spinlercb6b0592019-07-16 15:58:51 -0500133 // Now corrupt the private header
Matt Spinler42828bd2019-10-11 10:39:30 -0500134 data = pelDataFactory(TestPELType::pelSimple);
135 data.at(0) = 0;
136 pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500137
Matt Spinler97d19b42019-10-29 11:34:03 -0500138 EXPECT_FALSE(pel->privateHeader().valid());
139 EXPECT_TRUE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500140 EXPECT_FALSE(pel->valid());
141}
142
143TEST_F(PELTest, EmptyDataTest)
144{
145 std::vector<uint8_t> data;
146 auto pel = std::make_unique<PEL>(data);
147
Matt Spinler97d19b42019-10-29 11:34:03 -0500148 EXPECT_FALSE(pel->privateHeader().valid());
149 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500150 EXPECT_FALSE(pel->valid());
151}
Matt Spinlerb8323632019-09-20 15:11:04 -0500152
153TEST_F(PELTest, CreateFromRegistryTest)
154{
155 message::Entry regEntry;
156 uint64_t timestamp = 5;
157
158 regEntry.name = "test";
159 regEntry.subsystem = 5;
160 regEntry.actionFlags = 0xC000;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500161 regEntry.src.type = 0xBD;
162 regEntry.src.reasonCode = 0x1234;
Matt Spinlerb8323632019-09-20 15:11:04 -0500163
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600164 std::vector<std::string> data{"KEY1=VALUE1"};
165 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500166 NiceMock<MockDataInterface> dataIface;
167 PelFFDC ffdc;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500168
Sumit Kumar9d43a722021-08-24 09:46:19 -0500169 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
170 "system/entry"};
171 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
172 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
173
Matt Spinler56ad2a02020-03-26 14:00:52 -0500174 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
175 ad, ffdc, dataIface};
Matt Spinlerb8323632019-09-20 15:11:04 -0500176
177 EXPECT_TRUE(pel.valid());
Matt Spinler97d19b42019-10-29 11:34:03 -0500178 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
179 EXPECT_EQ(pel.userHeader().severity(), 0x40);
Matt Spinlerb8323632019-09-20 15:11:04 -0500180
Matt Spinlerbd716f02019-10-15 10:54:11 -0500181 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
182 "BD051234 ");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600183
184 // Check that certain optional sections have been created
185 size_t mtmsCount = 0;
186 size_t euhCount = 0;
187 size_t udCount = 0;
188
189 for (const auto& section : pel.optionalSections())
190 {
191 if (section->header().id ==
192 static_cast<uint16_t>(SectionID::failingMTMS))
193 {
194 mtmsCount++;
195 }
196 else if (section->header().id ==
197 static_cast<uint16_t>(SectionID::extendedUserHeader))
198 {
199 euhCount++;
200 }
201 else if (section->header().id ==
202 static_cast<uint16_t>(SectionID::userData))
203 {
204 udCount++;
205 }
206 }
207
208 EXPECT_EQ(mtmsCount, 1);
209 EXPECT_EQ(euhCount, 1);
210 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600211 ASSERT_FALSE(pel.isHwCalloutPresent());
Matt Spinler1f93c592020-09-10 10:43:08 -0500212
213 {
214 // The same thing, but without the action flags specified
215 // in the registry, so the constructor should set them.
216 regEntry.actionFlags = std::nullopt;
217
218 PEL pel2{
219 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
220 ad, ffdc, dataIface};
221
222 EXPECT_EQ(pel2.userHeader().actionFlags(), 0xA800);
223 }
Matt Spinlerb8323632019-09-20 15:11:04 -0500224}
Matt Spinler131870c2019-09-25 13:29:04 -0500225
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500226// Test that when the AdditionalData size is over 16KB that
227// the PEL that's created is exactly 16KB since the UserData
228// section that contains all that data was pruned.
229TEST_F(PELTest, CreateTooBigADTest)
230{
231 message::Entry regEntry;
232 uint64_t timestamp = 5;
233
234 regEntry.name = "test";
235 regEntry.subsystem = 5;
236 regEntry.actionFlags = 0xC000;
237 regEntry.src.type = 0xBD;
238 regEntry.src.reasonCode = 0x1234;
Matt Spinler56ad2a02020-03-26 14:00:52 -0500239 PelFFDC ffdc;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500240
241 // Over the 16KB max PEL size
242 std::string bigAD{"KEY1="};
243 bigAD += std::string(17000, 'G');
244
245 std::vector<std::string> data{bigAD};
246 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500247 NiceMock<MockDataInterface> dataIface;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500248
Sumit Kumar9d43a722021-08-24 09:46:19 -0500249 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
250 "system/entry"};
251 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
252 .WillOnce(Return(std::vector<bool>{false, false, false}));
253
Matt Spinler56ad2a02020-03-26 14:00:52 -0500254 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
255 ad, ffdc, dataIface};
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500256
257 EXPECT_TRUE(pel.valid());
258 EXPECT_EQ(pel.size(), 16384);
259
260 // Make sure that there are still 2 UD sections.
Matt Spinlerbe952d22022-07-01 11:30:11 -0500261 const auto& optSections = pel.optionalSections();
262 auto udCount = std::count_if(
263 optSections.begin(), optSections.end(), [](const auto& section) {
264 return section->header().id ==
265 static_cast<uint16_t>(SectionID::userData);
266 });
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500267
268 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
269}
270
Matt Spinler131870c2019-09-25 13:29:04 -0500271// Test that we'll create Generic optional sections for sections that
272// there aren't explicit classes for.
273TEST_F(PELTest, GenericSectionTest)
274{
Matt Spinler42828bd2019-10-11 10:39:30 -0500275 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500276
277 std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX'
278 0x00, 0x18, // Size
279 0x01, 0x02, // version, subtype
280 0x03, 0x04, // comp ID
281
282 // some data
283 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63,
284 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A,
285 0x00};
286
287 std::vector<uint8_t> section2{
288 0x59, 0x59, // ID 'YY'
289 0x00, 0x20, // Size
290 0x01, 0x02, // version, subtype
291 0x03, 0x04, // comp ID
292
293 // some data
294 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F,
295 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
296
297 // Add the new sections at the end
Matt Spinler42828bd2019-10-11 10:39:30 -0500298 data.insert(data.end(), section1.begin(), section1.end());
299 data.insert(data.end(), section2.begin(), section2.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500300
301 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500302 data.at(27) += 2;
303 auto origData = data;
Matt Spinler131870c2019-09-25 13:29:04 -0500304
Matt Spinler42828bd2019-10-11 10:39:30 -0500305 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500306
307 const auto& sections = pel.optionalSections();
308
309 bool foundXX = false;
310 bool foundYY = false;
311
312 // Check that we can find these 2 Generic sections
313 for (const auto& section : sections)
314 {
315 if (section->header().id == 0x5858)
316 {
317 foundXX = true;
318 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
319 }
320 else if (section->header().id == 0x5959)
321 {
322 foundYY = true;
323 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
324 }
325 }
326
327 EXPECT_TRUE(foundXX);
328 EXPECT_TRUE(foundYY);
Matt Spinler07eefc52019-09-26 11:18:26 -0500329
330 // Now flatten and check
331 auto newData = pel.data();
332
333 EXPECT_EQ(origData, newData);
Matt Spinler131870c2019-09-25 13:29:04 -0500334}
335
336// Test that an invalid section will still get a Generic object
337TEST_F(PELTest, InvalidGenericTest)
338{
Matt Spinler42828bd2019-10-11 10:39:30 -0500339 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500340
341 // Not a valid section
342 std::vector<uint8_t> section1{0x01, 0x02, 0x03};
343
Matt Spinler42828bd2019-10-11 10:39:30 -0500344 data.insert(data.end(), section1.begin(), section1.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500345
346 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500347 data.at(27) += 1;
Matt Spinler131870c2019-09-25 13:29:04 -0500348
Matt Spinler42828bd2019-10-11 10:39:30 -0500349 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500350 EXPECT_FALSE(pel.valid());
351
352 const auto& sections = pel.optionalSections();
353
354 bool foundGeneric = false;
355 for (const auto& section : sections)
356 {
357 if (dynamic_cast<Generic*>(section.get()) != nullptr)
358 {
359 foundGeneric = true;
360 EXPECT_EQ(section->valid(), false);
361 break;
362 }
363 }
364
365 EXPECT_TRUE(foundGeneric);
366}
Matt Spinlerafa857c2019-10-24 13:03:46 -0500367
368// Create a UserData section out of AdditionalData
369TEST_F(PELTest, MakeUDSectionTest)
370{
371 std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3",
372 "ESEL=TEST"};
373 AdditionalData additionalData{ad};
374
375 auto ud = util::makeADUserDataSection(additionalData);
376
377 EXPECT_TRUE(ud->valid());
378 EXPECT_EQ(ud->header().id, 0x5544);
379 EXPECT_EQ(ud->header().version, 0x01);
380 EXPECT_EQ(ud->header().subType, 0x01);
381 EXPECT_EQ(ud->header().componentID, 0x2000);
382
383 const auto& d = ud->data();
384
385 std::string jsonString{d.begin(), d.end()};
Matt Spinler53407be2019-11-18 09:16:31 -0600386
387 std::string expectedJSON =
Matt Spinlerafa857c2019-10-24 13:03:46 -0500388 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
Matt Spinler53407be2019-11-18 09:16:31 -0600389
390 // The actual data is null padded to a 4B boundary.
391 std::vector<uint8_t> expectedData;
392 expectedData.resize(52, '\0');
393 memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size());
394
395 EXPECT_EQ(d, expectedData);
Matt Spinlerafa857c2019-10-24 13:03:46 -0500396
397 // Ensure we can read this as JSON
398 auto newJSON = nlohmann::json::parse(jsonString);
399 EXPECT_EQ(newJSON["KEY1"], "VALUE1");
400 EXPECT_EQ(newJSON["KEY2"], "VALUE2");
401 EXPECT_EQ(newJSON["KEY3"], "VALUE3");
Matt Spinler97d19b42019-10-29 11:34:03 -0500402}
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600403
404// Create the UserData section that contains system info
Matt Spinler677381b2020-01-23 10:04:29 -0600405TEST_F(PELTest, SysInfoSectionTest)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600406{
407 MockDataInterface dataIface;
408
Matt Spinler677381b2020-01-23 10:04:29 -0600409 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600410 EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready"));
411 EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On"));
412 EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off"));
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500413 EXPECT_CALL(dataIface, getBootState())
414 .WillOnce(Return("State.SystemInitComplete"));
Ben Tynere32b7e72021-05-18 12:38:40 -0500415 EXPECT_CALL(dataIface, getSystemIMKeyword())
416 .WillOnce(Return(std::vector<uint8_t>{0, 1, 0x55, 0xAA}));
Matt Spinler677381b2020-01-23 10:04:29 -0600417
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600418 std::string pid = "_PID=" + std::to_string(getpid());
419 std::vector<std::string> ad{pid};
420 AdditionalData additionalData{ad};
421
422 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
423
424 EXPECT_TRUE(ud->valid());
425 EXPECT_EQ(ud->header().id, 0x5544);
426 EXPECT_EQ(ud->header().version, 0x01);
427 EXPECT_EQ(ud->header().subType, 0x01);
428 EXPECT_EQ(ud->header().componentID, 0x2000);
429
430 // Pull out the JSON data and check it.
431 const auto& d = ud->data();
432 std::string jsonString{d.begin(), d.end()};
433 auto json = nlohmann::json::parse(jsonString);
434
Patrick Williamsd9f0d642021-04-21 15:43:21 -0500435 // Ensure the 'Process Name' entry contains the name of this test
436 // executable.
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600437 auto name = json["Process Name"].get<std::string>();
Patrick Williamsd9f0d642021-04-21 15:43:21 -0500438 auto found = (name.find("pel_test") != std::string::npos) ||
439 (name.find("test-openpower-pels-pel") != std::string::npos);
440 EXPECT_TRUE(found);
441 // @TODO(stwcx): remove 'pel_test' when removing autotools.
Matt Spinler677381b2020-01-23 10:04:29 -0600442
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600443 auto version = json["FW Version ID"].get<std::string>();
Matt Spinler677381b2020-01-23 10:04:29 -0600444 EXPECT_EQ(version, "ABCD1234");
Matt Spinler4aa23a12020-02-03 15:05:09 -0600445
446 auto state = json["BMCState"].get<std::string>();
447 EXPECT_EQ(state, "Ready");
448
449 state = json["ChassisState"].get<std::string>();
450 EXPECT_EQ(state, "On");
451
452 state = json["HostState"].get<std::string>();
453 EXPECT_EQ(state, "Off");
Ben Tynere32b7e72021-05-18 12:38:40 -0500454
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500455 state = json["BootState"].get<std::string>();
456 EXPECT_EQ(state, "SystemInitComplete");
457
Ben Tynere32b7e72021-05-18 12:38:40 -0500458 auto keyword = json["System IM"].get<std::string>();
459 EXPECT_EQ(keyword, "000155AA");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600460}
Matt Spinlerce3f4502020-01-22 15:44:35 -0600461
462// Test that the sections that override
463// virtual std::optional<std::string> Section::getJSON() const
464// return valid JSON.
465TEST_F(PELTest, SectionJSONTest)
466{
467 auto data = pelDataFactory(TestPELType::pelSimple);
468 PEL pel{data};
469
470 // Check that all JSON returned from the sections is
471 // parseable by nlohmann::json, which will throw an
472 // exception and fail the test if there is a problem.
473
474 // The getJSON() response needs to be wrapped in a { } to make
475 // actual valid JSON (PEL::toJSON() usually handles that).
476
477 auto jsonString = pel.privateHeader().getJSON();
478
479 // PrivateHeader always prints JSON
480 ASSERT_TRUE(jsonString);
481 *jsonString = '{' + *jsonString + '}';
482 auto json = nlohmann::json::parse(*jsonString);
483
484 jsonString = pel.userHeader().getJSON();
485
486 // UserHeader always prints JSON
487 ASSERT_TRUE(jsonString);
488 *jsonString = '{' + *jsonString + '}';
489 json = nlohmann::json::parse(*jsonString);
490
491 for (const auto& section : pel.optionalSections())
492 {
493 // The optional sections may or may not have implemented getJSON().
494 jsonString = section->getJSON();
495 if (jsonString)
496 {
497 *jsonString = '{' + *jsonString + '}';
498 auto json = nlohmann::json::parse(*jsonString);
499 }
500 }
501}
Matt Spinler5b289b22020-03-26 14:27:19 -0500502
503PelFFDCfile getJSONFFDC(const fs::path& dir)
504{
505 PelFFDCfile ffdc;
506 ffdc.format = UserDataFormat::json;
507 ffdc.subType = 5;
508 ffdc.version = 42;
509
510 auto inputJSON = R"({
511 "key1": "value1",
512 "key2": 42,
513 "key3" : [1, 2, 3, 4, 5],
514 "key4": {"key5": "value5"}
515 })"_json;
516
517 // Write the JSON to a file and get its descriptor.
518 auto s = inputJSON.dump();
519 std::vector<uint8_t> data{s.begin(), s.end()};
520 ffdc.fd = writeFileAndGetFD(dir, data);
521
522 return ffdc;
523}
524
525TEST_F(PELTest, MakeJSONFileUDSectionTest)
526{
527 auto dir = makeTempDir();
528
529 {
530 auto ffdc = getJSONFFDC(dir);
531
532 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
533 close(ffdc.fd);
534 ASSERT_TRUE(ud);
535 ASSERT_TRUE(ud->valid());
536 EXPECT_EQ(ud->header().id, 0x5544);
537
538 EXPECT_EQ(ud->header().version,
539 static_cast<uint8_t>(UserDataFormatVersion::json));
540 EXPECT_EQ(ud->header().subType,
541 static_cast<uint8_t>(UserDataFormat::json));
542 EXPECT_EQ(ud->header().componentID,
543 static_cast<uint16_t>(ComponentID::phosphorLogging));
544
545 // Pull the JSON back out of the the UserData section
546 const auto& d = ud->data();
547 std::string js{d.begin(), d.end()};
548 auto json = nlohmann::json::parse(js);
549
550 EXPECT_EQ("value1", json["key1"].get<std::string>());
551 EXPECT_EQ(42, json["key2"].get<int>());
552
553 std::vector<int> key3Values{1, 2, 3, 4, 5};
554 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
555
556 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
557 auto actual = json["key4"].get<std::map<std::string, std::string>>();
558 EXPECT_EQ(key4Values, actual);
559 }
560
561 {
562 // A bad FD
563 PelFFDCfile ffdc;
564 ffdc.format = UserDataFormat::json;
565 ffdc.subType = 5;
566 ffdc.version = 42;
567 ffdc.fd = 10000;
568
569 // The section shouldn't get made
570 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
571 ASSERT_FALSE(ud);
572 }
573
574 fs::remove_all(dir);
575}
576
577PelFFDCfile getCBORFFDC(const fs::path& dir)
578{
579 PelFFDCfile ffdc;
580 ffdc.format = UserDataFormat::cbor;
581 ffdc.subType = 5;
582 ffdc.version = 42;
583
584 auto inputJSON = R"({
585 "key1": "value1",
586 "key2": 42,
587 "key3" : [1, 2, 3, 4, 5],
588 "key4": {"key5": "value5"}
589 })"_json;
590
591 // Convert the JSON to CBOR and write it to a file
592 auto data = nlohmann::json::to_cbor(inputJSON);
593 ffdc.fd = writeFileAndGetFD(dir, data);
594
595 return ffdc;
596}
597
598TEST_F(PELTest, MakeCBORFileUDSectionTest)
599{
600 auto dir = makeTempDir();
601
602 auto ffdc = getCBORFFDC(dir);
603 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
604 close(ffdc.fd);
605 ASSERT_TRUE(ud);
606 ASSERT_TRUE(ud->valid());
607 EXPECT_EQ(ud->header().id, 0x5544);
608
609 EXPECT_EQ(ud->header().version,
610 static_cast<uint8_t>(UserDataFormatVersion::cbor));
611 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor));
612 EXPECT_EQ(ud->header().componentID,
613 static_cast<uint16_t>(ComponentID::phosphorLogging));
614
615 // Pull the CBOR back out of the PEL section
616 // The number of pad bytes to make the section be 4B aligned
617 // was added at the end, read it and then remove it and the
618 // padding before parsing it.
619 auto data = ud->data();
620 Stream stream{data};
621 stream.offset(data.size() - 4);
622 uint32_t pad;
623 stream >> pad;
624
625 data.resize(data.size() - 4 - pad);
626
627 auto json = nlohmann::json::from_cbor(data);
628
629 EXPECT_EQ("value1", json["key1"].get<std::string>());
630 EXPECT_EQ(42, json["key2"].get<int>());
631
632 std::vector<int> key3Values{1, 2, 3, 4, 5};
633 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
634
635 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
636 auto actual = json["key4"].get<std::map<std::string, std::string>>();
637 EXPECT_EQ(key4Values, actual);
638
639 fs::remove_all(dir);
640}
641
642PelFFDCfile getTextFFDC(const fs::path& dir)
643{
644 PelFFDCfile ffdc;
645 ffdc.format = UserDataFormat::text;
646 ffdc.subType = 5;
647 ffdc.version = 42;
648
649 std::string text{"this is some text that will be used for FFDC"};
650 std::vector<uint8_t> data{text.begin(), text.end()};
651
652 ffdc.fd = writeFileAndGetFD(dir, data);
653
654 return ffdc;
655}
656
657TEST_F(PELTest, MakeTextFileUDSectionTest)
658{
659 auto dir = makeTempDir();
660
661 auto ffdc = getTextFFDC(dir);
662 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
663 close(ffdc.fd);
664 ASSERT_TRUE(ud);
665 ASSERT_TRUE(ud->valid());
666 EXPECT_EQ(ud->header().id, 0x5544);
667
668 EXPECT_EQ(ud->header().version,
669 static_cast<uint8_t>(UserDataFormatVersion::text));
670 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text));
671 EXPECT_EQ(ud->header().componentID,
672 static_cast<uint16_t>(ComponentID::phosphorLogging));
673
674 // Get the text back out
675 std::string text{ud->data().begin(), ud->data().end()};
676 EXPECT_EQ(text, "this is some text that will be used for FFDC");
677
678 fs::remove_all(dir);
679}
680
681PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data)
682{
683 PelFFDCfile ffdc;
684 ffdc.format = UserDataFormat::custom;
685 ffdc.subType = 5;
686 ffdc.version = 42;
687
688 ffdc.fd = writeFileAndGetFD(dir, data);
689
690 return ffdc;
691}
692
693TEST_F(PELTest, MakeCustomFileUDSectionTest)
694{
695 auto dir = makeTempDir();
696
697 {
698 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8};
699
700 auto ffdc = getCustomFFDC(dir, data);
701 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
702 close(ffdc.fd);
703 ASSERT_TRUE(ud);
704 ASSERT_TRUE(ud->valid());
705 EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size
706 EXPECT_EQ(ud->header().id, 0x5544);
707
708 EXPECT_EQ(ud->header().version, 42);
709 EXPECT_EQ(ud->header().subType, 5);
710 EXPECT_EQ(ud->header().componentID, 0x2002);
711
712 // Get the data back out
713 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
714 EXPECT_EQ(data, newData);
715 }
716
717 // Do the same thing again, but make it be non 4B aligned
718 // so the data gets padded.
719 {
720 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
721
722 auto ffdc = getCustomFFDC(dir, data);
723 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
724 close(ffdc.fd);
725 ASSERT_TRUE(ud);
726 ASSERT_TRUE(ud->valid());
727 EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size
728 EXPECT_EQ(ud->header().id, 0x5544);
729
730 EXPECT_EQ(ud->header().version, 42);
731 EXPECT_EQ(ud->header().subType, 5);
732 EXPECT_EQ(ud->header().componentID, 0x2002);
733
734 // Get the data back out
735 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
736
737 // pad the original to 12B so we can compare
738 data.push_back(0);
739 data.push_back(0);
740 data.push_back(0);
741
742 EXPECT_EQ(data, newData);
743 }
744
745 fs::remove_all(dir);
746}
747
748// Test Adding FFDC from files to a PEL
749TEST_F(PELTest, CreateWithFFDCTest)
750{
751 auto dir = makeTempDir();
752 message::Entry regEntry;
753 uint64_t timestamp = 5;
754
755 regEntry.name = "test";
756 regEntry.subsystem = 5;
757 regEntry.actionFlags = 0xC000;
758 regEntry.src.type = 0xBD;
759 regEntry.src.reasonCode = 0x1234;
760
761 std::vector<std::string> additionalData{"KEY1=VALUE1"};
762 AdditionalData ad{additionalData};
763 NiceMock<MockDataInterface> dataIface;
764 PelFFDC ffdc;
765
766 std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8};
767
768 // This will be trimmed when added
769 std::vector<uint8_t> hugeCustomData(17000, 0x42);
770
771 ffdc.emplace_back(std::move(getJSONFFDC(dir)));
772 ffdc.emplace_back(std::move(getCBORFFDC(dir)));
773 ffdc.emplace_back(std::move(getTextFFDC(dir)));
774 ffdc.emplace_back(std::move(getCustomFFDC(dir, customData)));
775 ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData)));
776
Sumit Kumar9d43a722021-08-24 09:46:19 -0500777 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
778 "system/entry"};
779 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
780 .WillOnce(Return(std::vector<bool>{false, false, false}));
781
Matt Spinler5b289b22020-03-26 14:27:19 -0500782 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
783 ad, ffdc, dataIface};
784
785 EXPECT_TRUE(pel.valid());
786
787 // Clipped to the max
788 EXPECT_EQ(pel.size(), 16384);
789
790 // Check for the FFDC sections
791 size_t udCount = 0;
792 Section* ud = nullptr;
793
794 for (const auto& section : pel.optionalSections())
795 {
796 if (section->header().id == static_cast<uint16_t>(SectionID::userData))
797 {
798 udCount++;
799 ud = section.get();
800 }
801 }
802
803 EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections
804
805 // Check the last section was trimmed to
806 // something a bit less that 17000.
807 EXPECT_GT(ud->header().size, 14000);
808 EXPECT_LT(ud->header().size, 16000);
809
810 fs::remove_all(dir);
811}
Matt Spinler0a90a852020-06-04 13:18:27 -0500812
813// Create a PEL with device callouts
814TEST_F(PELTest, CreateWithDevCalloutsTest)
815{
816 message::Entry regEntry;
817 uint64_t timestamp = 5;
818
819 regEntry.name = "test";
820 regEntry.subsystem = 5;
821 regEntry.actionFlags = 0xC000;
822 regEntry.src.type = 0xBD;
823 regEntry.src.reasonCode = 0x1234;
824
825 NiceMock<MockDataInterface> dataIface;
826 PelFFDC ffdc;
827
828 const auto calloutJSON = R"(
829 {
830 "I2C":
831 {
832 "14":
833 {
834 "114":
835 {
836 "Callouts":[
837 {
838 "Name": "/chassis/motherboard/cpu0",
839 "LocationCode": "P1",
840 "Priority": "H"
841 }
842 ],
843 "Dest": "proc 0 target"
844 }
845 }
846 }
847 })";
848
849 std::vector<std::string> names{"systemA"};
850 EXPECT_CALL(dataIface, getSystemNames)
851 .Times(2)
Matt Spinler1ab66962020-10-29 13:21:44 -0500852 .WillRepeatedly(Return(names));
Matt Spinler0a90a852020-06-04 13:18:27 -0500853
Matt Spinler0d92b522021-06-16 13:28:17 -0600854 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
855 .Times(1)
Matt Spinler0a90a852020-06-04 13:18:27 -0500856 .WillOnce(Return("UXXX-P1"));
857
Matt Spinler2f9225a2020-08-05 12:58:49 -0500858 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler0a90a852020-06-04 13:18:27 -0500859 .WillOnce(
860 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
861
862 EXPECT_CALL(
863 dataIface,
864 getHWCalloutFields(
865 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
866 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
867 SetArgReferee<3>("123456789ABC")));
868
Sumit Kumar9d43a722021-08-24 09:46:19 -0500869 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
870 "system/entry"};
871 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
872 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
873
Matt Spinler0a90a852020-06-04 13:18:27 -0500874 auto dataPath = getPELReadOnlyDataPath();
875 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
876 file << calloutJSON;
877 file.close();
878
879 {
880 std::vector<std::string> data{
881 "CALLOUT_ERRNO=5",
882 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
883 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
884
885 AdditionalData ad{data};
886
887 PEL pel{
888 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
889 ad, ffdc, dataIface};
890
891 ASSERT_TRUE(pel.primarySRC().value()->callouts());
892 auto& callouts = pel.primarySRC().value()->callouts()->callouts();
893 ASSERT_EQ(callouts.size(), 1);
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600894 ASSERT_TRUE(pel.isHwCalloutPresent());
Matt Spinler0a90a852020-06-04 13:18:27 -0500895
896 EXPECT_EQ(callouts[0]->priority(), 'H');
897 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1");
898
899 auto& fru = callouts[0]->fruIdentity();
900 EXPECT_EQ(fru->getPN().value(), "1234567");
901 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
902 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
903
904 const auto& section = pel.optionalSections().back();
905
906 ASSERT_EQ(section->header().id, 0x5544); // UD
907 auto ud = static_cast<UserData*>(section.get());
908
909 // Check that there was a UserData section added that
910 // contains debug details about the device.
911 const auto& d = ud->data();
912 std::string jsonString{d.begin(), d.end()};
913 auto actualJSON = nlohmann::json::parse(jsonString);
914
915 auto expectedJSON = R"(
916 {
917 "PEL Internal Debug Data": {
918 "SRC": [
919 "I2C: bus: 14 address: 114 dest: proc 0 target"
920 ]
921 }
922 }
923 )"_json;
924
925 EXPECT_EQ(actualJSON, expectedJSON);
926 }
927
928 {
929 // Device path not found (wrong i2c addr), so no callouts
930 std::vector<std::string> data{
931 "CALLOUT_ERRNO=5",
932 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
933 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0099"};
934
935 AdditionalData ad{data};
936
937 PEL pel{
938 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
939 ad, ffdc, dataIface};
940
941 // no callouts
942 EXPECT_FALSE(pel.primarySRC().value()->callouts());
943
944 // Now check that there was a UserData section
945 // that contains the lookup error.
946 const auto& section = pel.optionalSections().back();
947
948 ASSERT_EQ(section->header().id, 0x5544); // UD
949 auto ud = static_cast<UserData*>(section.get());
950
951 const auto& d = ud->data();
952
953 std::string jsonString{d.begin(), d.end()};
954
955 auto actualJSON = nlohmann::json::parse(jsonString);
956
957 auto expectedJSON =
958 "{\"PEL Internal Debug Data\":{\"SRC\":"
959 "[\"Problem looking up I2C callouts on 14 153: "
960 "[json.exception.out_of_range.403] key '153' not found\"]}}"_json;
961
962 EXPECT_EQ(actualJSON, expectedJSON);
963 }
964
965 fs::remove_all(dataPath);
966}
Matt Spinlere513dbc2020-08-27 11:14:17 -0500967
968// Test PELs when the callouts are passed in using a JSON file.
969TEST_F(PELTest, CreateWithJSONCalloutsTest)
970{
971 PelFFDCfile ffdcFile;
972 ffdcFile.format = UserDataFormat::json;
973 ffdcFile.subType = 0xCA; // Callout JSON
974 ffdcFile.version = 1;
975
976 // Write these callouts to a JSON file and pass it into
977 // the PEL as an FFDC file.
978 auto inputJSON = R"([
979 {
980 "Priority": "H",
981 "LocationCode": "P0-C1"
982 },
983 {
984 "Priority": "M",
985 "Procedure": "PROCEDURE"
986 }
987 ])"_json;
988
989 auto s = inputJSON.dump();
990 std::vector<uint8_t> data{s.begin(), s.end()};
991 auto dir = makeTempDir();
992 ffdcFile.fd = writeFileAndGetFD(dir, data);
993
994 PelFFDC ffdc;
995 ffdc.push_back(std::move(ffdcFile));
996
997 AdditionalData ad;
998 NiceMock<MockDataInterface> dataIface;
999
1000 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1001 .Times(1)
1002 .WillOnce(Return("UXXX-P0-C1"));
1003 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1004 .Times(1)
1005 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1006 EXPECT_CALL(dataIface, getHWCalloutFields(
1007 "/inv/system/chassis/motherboard/bmc", _, _, _))
1008 .Times(1)
1009 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1010 SetArgReferee<3>("123456789ABC")));
1011
Sumit Kumar9d43a722021-08-24 09:46:19 -05001012 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1013 "system/entry"};
1014 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1015 .WillOnce(Return(std::vector<bool>{false, false, false}));
1016
Matt Spinlere513dbc2020-08-27 11:14:17 -05001017 message::Entry regEntry;
1018 regEntry.name = "test";
1019 regEntry.subsystem = 5;
1020 regEntry.actionFlags = 0xC000;
1021 regEntry.src.type = 0xBD;
1022 regEntry.src.reasonCode = 0x1234;
1023
1024 PEL pel{regEntry, 42, 5, phosphor::logging::Entry::Level::Error,
1025 ad, ffdc, dataIface};
1026
1027 ASSERT_TRUE(pel.valid());
1028 ASSERT_TRUE(pel.primarySRC().value()->callouts());
1029 const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
1030 ASSERT_EQ(callouts.size(), 2);
Andrew Geisslerf8e750d2022-01-14 14:56:13 -06001031 ASSERT_TRUE(pel.isHwCalloutPresent());
Matt Spinlere513dbc2020-08-27 11:14:17 -05001032
1033 {
1034 EXPECT_EQ(callouts[0]->priority(), 'H');
1035 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1036
1037 auto& fru = callouts[0]->fruIdentity();
1038 EXPECT_EQ(fru->getPN().value(), "1234567");
1039 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1040 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1041 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1042 }
1043 {
1044 EXPECT_EQ(callouts[1]->priority(), 'M');
1045 EXPECT_EQ(callouts[1]->locationCode(), "");
1046
1047 auto& fru = callouts[1]->fruIdentity();
1048 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1049 EXPECT_EQ(fru->failingComponentType(),
1050 src::FRUIdentity::maintenanceProc);
1051 }
1052 fs::remove_all(dir);
1053}
Andrew Geisslerf8e750d2022-01-14 14:56:13 -06001054
1055// Test PELs with symblic FRU callout.
1056TEST_F(PELTest, CreateWithJSONSymblicCalloutTest)
1057{
1058 PelFFDCfile ffdcFile;
1059 ffdcFile.format = UserDataFormat::json;
1060 ffdcFile.subType = 0xCA; // Callout JSON
1061 ffdcFile.version = 1;
1062
1063 // Write these callouts to a JSON file and pass it into
1064 // the PEL as an FFDC file.
1065 auto inputJSON = R"([
1066 {
1067 "Priority": "M",
1068 "Procedure": "SVCDOCS"
1069 }
1070 ])"_json;
1071
1072 auto s = inputJSON.dump();
1073 std::vector<uint8_t> data{s.begin(), s.end()};
1074 auto dir = makeTempDir();
1075 ffdcFile.fd = writeFileAndGetFD(dir, data);
1076
1077 PelFFDC ffdc;
1078 ffdc.push_back(std::move(ffdcFile));
1079
1080 AdditionalData ad;
1081 NiceMock<MockDataInterface> dataIface;
1082
1083 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1084 "system/entry"};
1085 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1086 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1087
1088 message::Entry regEntry;
1089 regEntry.name = "test";
1090 regEntry.subsystem = 5;
1091 regEntry.actionFlags = 0xC000;
1092 regEntry.src.type = 0xBD;
1093 regEntry.src.reasonCode = 0x1234;
1094
1095 PEL pel{regEntry, 42, 5, phosphor::logging::Entry::Level::Error,
1096 ad, ffdc, dataIface};
1097
1098 ASSERT_TRUE(pel.valid());
1099 ASSERT_TRUE(pel.primarySRC().value()->callouts());
1100 const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
1101 ASSERT_EQ(callouts.size(), 1);
1102 ASSERT_FALSE(pel.isHwCalloutPresent());
1103
1104 {
1105 EXPECT_EQ(callouts[0]->priority(), 'M');
1106 EXPECT_EQ(callouts[0]->locationCode(), "");
1107
1108 auto& fru = callouts[0]->fruIdentity();
1109 EXPECT_EQ(fru->getMaintProc().value(), "SVCDOCS");
1110 }
1111 fs::remove_all(dir);
1112}