blob: d5cd373cb49dfc5922884ffac53ac782875e464a [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
36{
37};
38
Matt Spinler5b289b22020-03-26 14:27:19 -050039fs::path makeTempDir()
40{
41 char path[] = "/tmp/tempdirXXXXXX";
42 std::filesystem::path dir = mkdtemp(path);
43 return dir;
44}
45
46int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data)
47{
48 static size_t count = 0;
49 fs::path path = dir / (std::string{"file"} + std::to_string(count));
50 std::ofstream stream{path};
51 count++;
52
53 stream.write(reinterpret_cast<const char*>(data.data()), data.size());
54 stream.close();
55
56 FILE* fp = fopen(path.c_str(), "r");
57 return fileno(fp);
58}
59
Matt Spinlercb6b0592019-07-16 15:58:51 -050060TEST_F(PELTest, FlattenTest)
61{
Matt Spinler42828bd2019-10-11 10:39:30 -050062 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler42828bd2019-10-11 10:39:30 -050063 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050064
65 // Check a few fields
66 EXPECT_TRUE(pel->valid());
67 EXPECT_EQ(pel->id(), 0x80818283);
68 EXPECT_EQ(pel->plid(), 0x50515253);
Matt Spinler97d19b42019-10-29 11:34:03 -050069 EXPECT_EQ(pel->userHeader().subsystem(), 0x10);
70 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0);
Matt Spinlercb6b0592019-07-16 15:58:51 -050071
72 // Test that data in == data out
73 auto flattenedData = pel->data();
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060074 EXPECT_EQ(data, flattenedData);
75 EXPECT_EQ(flattenedData.size(), pel->size());
Matt Spinlercb6b0592019-07-16 15:58:51 -050076}
77
78TEST_F(PELTest, CommitTimeTest)
79{
Matt Spinler42828bd2019-10-11 10:39:30 -050080 auto data = pelDataFactory(TestPELType::pelSimple);
81 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050082
83 auto origTime = pel->commitTime();
84 pel->setCommitTime();
85 auto newTime = pel->commitTime();
86
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060087 EXPECT_NE(origTime, newTime);
Matt Spinlercb6b0592019-07-16 15:58:51 -050088
89 // Make a new PEL and check new value is still there
90 auto newData = pel->data();
91 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060092 EXPECT_EQ(newTime, newPel->commitTime());
Matt Spinlercb6b0592019-07-16 15:58:51 -050093}
94
95TEST_F(PELTest, AssignIDTest)
96{
Matt Spinler42828bd2019-10-11 10:39:30 -050097 auto data = pelDataFactory(TestPELType::pelSimple);
98 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050099
100 auto origID = pel->id();
101 pel->assignID();
102 auto newID = pel->id();
103
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600104 EXPECT_NE(origID, newID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500105
106 // Make a new PEL and check new value is still there
107 auto newData = pel->data();
108 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600109 EXPECT_EQ(newID, newPel->id());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500110}
111
112TEST_F(PELTest, WithLogIDTest)
113{
Matt Spinler42828bd2019-10-11 10:39:30 -0500114 auto data = pelDataFactory(TestPELType::pelSimple);
115 auto pel = std::make_unique<PEL>(data, 0x42);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500116
117 EXPECT_TRUE(pel->valid());
118 EXPECT_EQ(pel->obmcLogID(), 0x42);
119}
120
121TEST_F(PELTest, InvalidPELTest)
122{
Matt Spinler42828bd2019-10-11 10:39:30 -0500123 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500124
125 // Too small
Matt Spinler42828bd2019-10-11 10:39:30 -0500126 data.resize(PrivateHeader::flattenedSize());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500127
Matt Spinler42828bd2019-10-11 10:39:30 -0500128 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500129
Matt Spinler97d19b42019-10-29 11:34:03 -0500130 EXPECT_TRUE(pel->privateHeader().valid());
131 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500132 EXPECT_FALSE(pel->valid());
133
Matt Spinlercb6b0592019-07-16 15:58:51 -0500134 // Now corrupt the private header
Matt Spinler42828bd2019-10-11 10:39:30 -0500135 data = pelDataFactory(TestPELType::pelSimple);
136 data.at(0) = 0;
137 pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500138
Matt Spinler97d19b42019-10-29 11:34:03 -0500139 EXPECT_FALSE(pel->privateHeader().valid());
140 EXPECT_TRUE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500141 EXPECT_FALSE(pel->valid());
142}
143
144TEST_F(PELTest, EmptyDataTest)
145{
146 std::vector<uint8_t> data;
147 auto pel = std::make_unique<PEL>(data);
148
Matt Spinler97d19b42019-10-29 11:34:03 -0500149 EXPECT_FALSE(pel->privateHeader().valid());
150 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500151 EXPECT_FALSE(pel->valid());
152}
Matt Spinlerb8323632019-09-20 15:11:04 -0500153
154TEST_F(PELTest, CreateFromRegistryTest)
155{
156 message::Entry regEntry;
157 uint64_t timestamp = 5;
158
159 regEntry.name = "test";
160 regEntry.subsystem = 5;
161 regEntry.actionFlags = 0xC000;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500162 regEntry.src.type = 0xBD;
163 regEntry.src.reasonCode = 0x1234;
Matt Spinlerb8323632019-09-20 15:11:04 -0500164
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600165 std::vector<std::string> data{"KEY1=VALUE1"};
166 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500167 NiceMock<MockDataInterface> dataIface;
168 PelFFDC ffdc;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500169
Sumit Kumar9d43a722021-08-24 09:46:19 -0500170 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
171 "system/entry"};
172 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
173 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
174
Matt Spinler56ad2a02020-03-26 14:00:52 -0500175 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
176 ad, ffdc, dataIface};
Matt Spinlerb8323632019-09-20 15:11:04 -0500177
178 EXPECT_TRUE(pel.valid());
Matt Spinler97d19b42019-10-29 11:34:03 -0500179 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
180 EXPECT_EQ(pel.userHeader().severity(), 0x40);
Matt Spinlerb8323632019-09-20 15:11:04 -0500181
Matt Spinlerbd716f02019-10-15 10:54:11 -0500182 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
183 "BD051234 ");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600184
185 // Check that certain optional sections have been created
186 size_t mtmsCount = 0;
187 size_t euhCount = 0;
188 size_t udCount = 0;
189
190 for (const auto& section : pel.optionalSections())
191 {
192 if (section->header().id ==
193 static_cast<uint16_t>(SectionID::failingMTMS))
194 {
195 mtmsCount++;
196 }
197 else if (section->header().id ==
198 static_cast<uint16_t>(SectionID::extendedUserHeader))
199 {
200 euhCount++;
201 }
202 else if (section->header().id ==
203 static_cast<uint16_t>(SectionID::userData))
204 {
205 udCount++;
206 }
207 }
208
209 EXPECT_EQ(mtmsCount, 1);
210 EXPECT_EQ(euhCount, 1);
211 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600212 ASSERT_FALSE(pel.isHwCalloutPresent());
Matt Spinler1f93c592020-09-10 10:43:08 -0500213
214 {
215 // The same thing, but without the action flags specified
216 // in the registry, so the constructor should set them.
217 regEntry.actionFlags = std::nullopt;
218
219 PEL pel2{
220 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
221 ad, ffdc, dataIface};
222
223 EXPECT_EQ(pel2.userHeader().actionFlags(), 0xA800);
224 }
Matt Spinlerb8323632019-09-20 15:11:04 -0500225}
Matt Spinler131870c2019-09-25 13:29:04 -0500226
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500227// Test that when the AdditionalData size is over 16KB that
228// the PEL that's created is exactly 16KB since the UserData
229// section that contains all that data was pruned.
230TEST_F(PELTest, CreateTooBigADTest)
231{
232 message::Entry regEntry;
233 uint64_t timestamp = 5;
234
235 regEntry.name = "test";
236 regEntry.subsystem = 5;
237 regEntry.actionFlags = 0xC000;
238 regEntry.src.type = 0xBD;
239 regEntry.src.reasonCode = 0x1234;
Matt Spinler56ad2a02020-03-26 14:00:52 -0500240 PelFFDC ffdc;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500241
242 // Over the 16KB max PEL size
243 std::string bigAD{"KEY1="};
244 bigAD += std::string(17000, 'G');
245
246 std::vector<std::string> data{bigAD};
247 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500248 NiceMock<MockDataInterface> dataIface;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500249
Sumit Kumar9d43a722021-08-24 09:46:19 -0500250 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
251 "system/entry"};
252 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
253 .WillOnce(Return(std::vector<bool>{false, false, false}));
254
Matt Spinler56ad2a02020-03-26 14:00:52 -0500255 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
256 ad, ffdc, dataIface};
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500257
258 EXPECT_TRUE(pel.valid());
259 EXPECT_EQ(pel.size(), 16384);
260
261 // Make sure that there are still 2 UD sections.
Matt Spinlerbe952d22022-07-01 11:30:11 -0500262 const auto& optSections = pel.optionalSections();
263 auto udCount = std::count_if(
264 optSections.begin(), optSections.end(), [](const auto& section) {
265 return section->header().id ==
266 static_cast<uint16_t>(SectionID::userData);
267 });
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500268
269 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
270}
271
Matt Spinler131870c2019-09-25 13:29:04 -0500272// Test that we'll create Generic optional sections for sections that
273// there aren't explicit classes for.
274TEST_F(PELTest, GenericSectionTest)
275{
Matt Spinler42828bd2019-10-11 10:39:30 -0500276 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500277
278 std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX'
279 0x00, 0x18, // Size
280 0x01, 0x02, // version, subtype
281 0x03, 0x04, // comp ID
282
283 // some data
284 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63,
285 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A,
286 0x00};
287
288 std::vector<uint8_t> section2{
289 0x59, 0x59, // ID 'YY'
290 0x00, 0x20, // Size
291 0x01, 0x02, // version, subtype
292 0x03, 0x04, // comp ID
293
294 // some data
295 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F,
296 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
297
298 // Add the new sections at the end
Matt Spinler42828bd2019-10-11 10:39:30 -0500299 data.insert(data.end(), section1.begin(), section1.end());
300 data.insert(data.end(), section2.begin(), section2.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500301
302 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500303 data.at(27) += 2;
304 auto origData = data;
Matt Spinler131870c2019-09-25 13:29:04 -0500305
Matt Spinler42828bd2019-10-11 10:39:30 -0500306 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500307
308 const auto& sections = pel.optionalSections();
309
310 bool foundXX = false;
311 bool foundYY = false;
312
313 // Check that we can find these 2 Generic sections
314 for (const auto& section : sections)
315 {
316 if (section->header().id == 0x5858)
317 {
318 foundXX = true;
319 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
320 }
321 else if (section->header().id == 0x5959)
322 {
323 foundYY = true;
324 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
325 }
326 }
327
328 EXPECT_TRUE(foundXX);
329 EXPECT_TRUE(foundYY);
Matt Spinler07eefc52019-09-26 11:18:26 -0500330
331 // Now flatten and check
332 auto newData = pel.data();
333
334 EXPECT_EQ(origData, newData);
Matt Spinler131870c2019-09-25 13:29:04 -0500335}
336
337// Test that an invalid section will still get a Generic object
338TEST_F(PELTest, InvalidGenericTest)
339{
Matt Spinler42828bd2019-10-11 10:39:30 -0500340 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500341
342 // Not a valid section
343 std::vector<uint8_t> section1{0x01, 0x02, 0x03};
344
Matt Spinler42828bd2019-10-11 10:39:30 -0500345 data.insert(data.end(), section1.begin(), section1.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500346
347 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500348 data.at(27) += 1;
Matt Spinler131870c2019-09-25 13:29:04 -0500349
Matt Spinler42828bd2019-10-11 10:39:30 -0500350 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500351 EXPECT_FALSE(pel.valid());
352
353 const auto& sections = pel.optionalSections();
354
355 bool foundGeneric = false;
356 for (const auto& section : sections)
357 {
358 if (dynamic_cast<Generic*>(section.get()) != nullptr)
359 {
360 foundGeneric = true;
361 EXPECT_EQ(section->valid(), false);
362 break;
363 }
364 }
365
366 EXPECT_TRUE(foundGeneric);
367}
Matt Spinlerafa857c2019-10-24 13:03:46 -0500368
369// Create a UserData section out of AdditionalData
370TEST_F(PELTest, MakeUDSectionTest)
371{
372 std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3",
373 "ESEL=TEST"};
374 AdditionalData additionalData{ad};
375
376 auto ud = util::makeADUserDataSection(additionalData);
377
378 EXPECT_TRUE(ud->valid());
379 EXPECT_EQ(ud->header().id, 0x5544);
380 EXPECT_EQ(ud->header().version, 0x01);
381 EXPECT_EQ(ud->header().subType, 0x01);
382 EXPECT_EQ(ud->header().componentID, 0x2000);
383
384 const auto& d = ud->data();
385
386 std::string jsonString{d.begin(), d.end()};
Matt Spinler53407be2019-11-18 09:16:31 -0600387
388 std::string expectedJSON =
Matt Spinlerafa857c2019-10-24 13:03:46 -0500389 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
Matt Spinler53407be2019-11-18 09:16:31 -0600390
391 // The actual data is null padded to a 4B boundary.
392 std::vector<uint8_t> expectedData;
393 expectedData.resize(52, '\0');
394 memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size());
395
396 EXPECT_EQ(d, expectedData);
Matt Spinlerafa857c2019-10-24 13:03:46 -0500397
398 // Ensure we can read this as JSON
399 auto newJSON = nlohmann::json::parse(jsonString);
400 EXPECT_EQ(newJSON["KEY1"], "VALUE1");
401 EXPECT_EQ(newJSON["KEY2"], "VALUE2");
402 EXPECT_EQ(newJSON["KEY3"], "VALUE3");
Matt Spinler97d19b42019-10-29 11:34:03 -0500403}
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600404
405// Create the UserData section that contains system info
Matt Spinler677381b2020-01-23 10:04:29 -0600406TEST_F(PELTest, SysInfoSectionTest)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600407{
408 MockDataInterface dataIface;
409
Matt Spinler677381b2020-01-23 10:04:29 -0600410 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600411 EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready"));
412 EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On"));
413 EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off"));
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500414 EXPECT_CALL(dataIface, getBootState())
415 .WillOnce(Return("State.SystemInitComplete"));
Ben Tynere32b7e72021-05-18 12:38:40 -0500416 EXPECT_CALL(dataIface, getSystemIMKeyword())
417 .WillOnce(Return(std::vector<uint8_t>{0, 1, 0x55, 0xAA}));
Matt Spinler677381b2020-01-23 10:04:29 -0600418
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600419 std::string pid = "_PID=" + std::to_string(getpid());
420 std::vector<std::string> ad{pid};
421 AdditionalData additionalData{ad};
422
423 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
424
425 EXPECT_TRUE(ud->valid());
426 EXPECT_EQ(ud->header().id, 0x5544);
427 EXPECT_EQ(ud->header().version, 0x01);
428 EXPECT_EQ(ud->header().subType, 0x01);
429 EXPECT_EQ(ud->header().componentID, 0x2000);
430
431 // Pull out the JSON data and check it.
432 const auto& d = ud->data();
433 std::string jsonString{d.begin(), d.end()};
434 auto json = nlohmann::json::parse(jsonString);
435
Patrick Williamsd9f0d642021-04-21 15:43:21 -0500436 // Ensure the 'Process Name' entry contains the name of this test
437 // executable.
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600438 auto name = json["Process Name"].get<std::string>();
Patrick Williamsd9f0d642021-04-21 15:43:21 -0500439 auto found = (name.find("pel_test") != std::string::npos) ||
440 (name.find("test-openpower-pels-pel") != std::string::npos);
441 EXPECT_TRUE(found);
442 // @TODO(stwcx): remove 'pel_test' when removing autotools.
Matt Spinler677381b2020-01-23 10:04:29 -0600443
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600444 auto version = json["FW Version ID"].get<std::string>();
Matt Spinler677381b2020-01-23 10:04:29 -0600445 EXPECT_EQ(version, "ABCD1234");
Matt Spinler4aa23a12020-02-03 15:05:09 -0600446
447 auto state = json["BMCState"].get<std::string>();
448 EXPECT_EQ(state, "Ready");
449
450 state = json["ChassisState"].get<std::string>();
451 EXPECT_EQ(state, "On");
452
453 state = json["HostState"].get<std::string>();
454 EXPECT_EQ(state, "Off");
Ben Tynere32b7e72021-05-18 12:38:40 -0500455
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500456 state = json["BootState"].get<std::string>();
457 EXPECT_EQ(state, "SystemInitComplete");
458
Ben Tynere32b7e72021-05-18 12:38:40 -0500459 auto keyword = json["System IM"].get<std::string>();
460 EXPECT_EQ(keyword, "000155AA");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600461}
Matt Spinlerce3f4502020-01-22 15:44:35 -0600462
463// Test that the sections that override
464// virtual std::optional<std::string> Section::getJSON() const
465// return valid JSON.
466TEST_F(PELTest, SectionJSONTest)
467{
468 auto data = pelDataFactory(TestPELType::pelSimple);
469 PEL pel{data};
470
471 // Check that all JSON returned from the sections is
472 // parseable by nlohmann::json, which will throw an
473 // exception and fail the test if there is a problem.
474
475 // The getJSON() response needs to be wrapped in a { } to make
476 // actual valid JSON (PEL::toJSON() usually handles that).
477
478 auto jsonString = pel.privateHeader().getJSON();
479
480 // PrivateHeader always prints JSON
481 ASSERT_TRUE(jsonString);
482 *jsonString = '{' + *jsonString + '}';
483 auto json = nlohmann::json::parse(*jsonString);
484
485 jsonString = pel.userHeader().getJSON();
486
487 // UserHeader always prints JSON
488 ASSERT_TRUE(jsonString);
489 *jsonString = '{' + *jsonString + '}';
490 json = nlohmann::json::parse(*jsonString);
491
492 for (const auto& section : pel.optionalSections())
493 {
494 // The optional sections may or may not have implemented getJSON().
495 jsonString = section->getJSON();
496 if (jsonString)
497 {
498 *jsonString = '{' + *jsonString + '}';
499 auto json = nlohmann::json::parse(*jsonString);
500 }
501 }
502}
Matt Spinler5b289b22020-03-26 14:27:19 -0500503
504PelFFDCfile getJSONFFDC(const fs::path& dir)
505{
506 PelFFDCfile ffdc;
507 ffdc.format = UserDataFormat::json;
508 ffdc.subType = 5;
509 ffdc.version = 42;
510
511 auto inputJSON = R"({
512 "key1": "value1",
513 "key2": 42,
514 "key3" : [1, 2, 3, 4, 5],
515 "key4": {"key5": "value5"}
516 })"_json;
517
518 // Write the JSON to a file and get its descriptor.
519 auto s = inputJSON.dump();
520 std::vector<uint8_t> data{s.begin(), s.end()};
521 ffdc.fd = writeFileAndGetFD(dir, data);
522
523 return ffdc;
524}
525
526TEST_F(PELTest, MakeJSONFileUDSectionTest)
527{
528 auto dir = makeTempDir();
529
530 {
531 auto ffdc = getJSONFFDC(dir);
532
533 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
534 close(ffdc.fd);
535 ASSERT_TRUE(ud);
536 ASSERT_TRUE(ud->valid());
537 EXPECT_EQ(ud->header().id, 0x5544);
538
539 EXPECT_EQ(ud->header().version,
540 static_cast<uint8_t>(UserDataFormatVersion::json));
541 EXPECT_EQ(ud->header().subType,
542 static_cast<uint8_t>(UserDataFormat::json));
543 EXPECT_EQ(ud->header().componentID,
544 static_cast<uint16_t>(ComponentID::phosphorLogging));
545
546 // Pull the JSON back out of the the UserData section
547 const auto& d = ud->data();
548 std::string js{d.begin(), d.end()};
549 auto json = nlohmann::json::parse(js);
550
551 EXPECT_EQ("value1", json["key1"].get<std::string>());
552 EXPECT_EQ(42, json["key2"].get<int>());
553
554 std::vector<int> key3Values{1, 2, 3, 4, 5};
555 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
556
557 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
558 auto actual = json["key4"].get<std::map<std::string, std::string>>();
559 EXPECT_EQ(key4Values, actual);
560 }
561
562 {
563 // A bad FD
564 PelFFDCfile ffdc;
565 ffdc.format = UserDataFormat::json;
566 ffdc.subType = 5;
567 ffdc.version = 42;
568 ffdc.fd = 10000;
569
570 // The section shouldn't get made
571 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
572 ASSERT_FALSE(ud);
573 }
574
575 fs::remove_all(dir);
576}
577
578PelFFDCfile getCBORFFDC(const fs::path& dir)
579{
580 PelFFDCfile ffdc;
581 ffdc.format = UserDataFormat::cbor;
582 ffdc.subType = 5;
583 ffdc.version = 42;
584
585 auto inputJSON = R"({
586 "key1": "value1",
587 "key2": 42,
588 "key3" : [1, 2, 3, 4, 5],
589 "key4": {"key5": "value5"}
590 })"_json;
591
592 // Convert the JSON to CBOR and write it to a file
593 auto data = nlohmann::json::to_cbor(inputJSON);
594 ffdc.fd = writeFileAndGetFD(dir, data);
595
596 return ffdc;
597}
598
599TEST_F(PELTest, MakeCBORFileUDSectionTest)
600{
601 auto dir = makeTempDir();
602
603 auto ffdc = getCBORFFDC(dir);
604 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
605 close(ffdc.fd);
606 ASSERT_TRUE(ud);
607 ASSERT_TRUE(ud->valid());
608 EXPECT_EQ(ud->header().id, 0x5544);
609
610 EXPECT_EQ(ud->header().version,
611 static_cast<uint8_t>(UserDataFormatVersion::cbor));
612 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor));
613 EXPECT_EQ(ud->header().componentID,
614 static_cast<uint16_t>(ComponentID::phosphorLogging));
615
616 // Pull the CBOR back out of the PEL section
617 // The number of pad bytes to make the section be 4B aligned
618 // was added at the end, read it and then remove it and the
619 // padding before parsing it.
620 auto data = ud->data();
621 Stream stream{data};
622 stream.offset(data.size() - 4);
623 uint32_t pad;
624 stream >> pad;
625
626 data.resize(data.size() - 4 - pad);
627
628 auto json = nlohmann::json::from_cbor(data);
629
630 EXPECT_EQ("value1", json["key1"].get<std::string>());
631 EXPECT_EQ(42, json["key2"].get<int>());
632
633 std::vector<int> key3Values{1, 2, 3, 4, 5};
634 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
635
636 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
637 auto actual = json["key4"].get<std::map<std::string, std::string>>();
638 EXPECT_EQ(key4Values, actual);
639
640 fs::remove_all(dir);
641}
642
643PelFFDCfile getTextFFDC(const fs::path& dir)
644{
645 PelFFDCfile ffdc;
646 ffdc.format = UserDataFormat::text;
647 ffdc.subType = 5;
648 ffdc.version = 42;
649
650 std::string text{"this is some text that will be used for FFDC"};
651 std::vector<uint8_t> data{text.begin(), text.end()};
652
653 ffdc.fd = writeFileAndGetFD(dir, data);
654
655 return ffdc;
656}
657
658TEST_F(PELTest, MakeTextFileUDSectionTest)
659{
660 auto dir = makeTempDir();
661
662 auto ffdc = getTextFFDC(dir);
663 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
664 close(ffdc.fd);
665 ASSERT_TRUE(ud);
666 ASSERT_TRUE(ud->valid());
667 EXPECT_EQ(ud->header().id, 0x5544);
668
669 EXPECT_EQ(ud->header().version,
670 static_cast<uint8_t>(UserDataFormatVersion::text));
671 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text));
672 EXPECT_EQ(ud->header().componentID,
673 static_cast<uint16_t>(ComponentID::phosphorLogging));
674
675 // Get the text back out
676 std::string text{ud->data().begin(), ud->data().end()};
677 EXPECT_EQ(text, "this is some text that will be used for FFDC");
678
679 fs::remove_all(dir);
680}
681
682PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data)
683{
684 PelFFDCfile ffdc;
685 ffdc.format = UserDataFormat::custom;
686 ffdc.subType = 5;
687 ffdc.version = 42;
688
689 ffdc.fd = writeFileAndGetFD(dir, data);
690
691 return ffdc;
692}
693
694TEST_F(PELTest, MakeCustomFileUDSectionTest)
695{
696 auto dir = makeTempDir();
697
698 {
699 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8};
700
701 auto ffdc = getCustomFFDC(dir, data);
702 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
703 close(ffdc.fd);
704 ASSERT_TRUE(ud);
705 ASSERT_TRUE(ud->valid());
706 EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size
707 EXPECT_EQ(ud->header().id, 0x5544);
708
709 EXPECT_EQ(ud->header().version, 42);
710 EXPECT_EQ(ud->header().subType, 5);
711 EXPECT_EQ(ud->header().componentID, 0x2002);
712
713 // Get the data back out
714 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
715 EXPECT_EQ(data, newData);
716 }
717
718 // Do the same thing again, but make it be non 4B aligned
719 // so the data gets padded.
720 {
721 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
722
723 auto ffdc = getCustomFFDC(dir, data);
724 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
725 close(ffdc.fd);
726 ASSERT_TRUE(ud);
727 ASSERT_TRUE(ud->valid());
728 EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size
729 EXPECT_EQ(ud->header().id, 0x5544);
730
731 EXPECT_EQ(ud->header().version, 42);
732 EXPECT_EQ(ud->header().subType, 5);
733 EXPECT_EQ(ud->header().componentID, 0x2002);
734
735 // Get the data back out
736 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
737
738 // pad the original to 12B so we can compare
739 data.push_back(0);
740 data.push_back(0);
741 data.push_back(0);
742
743 EXPECT_EQ(data, newData);
744 }
745
746 fs::remove_all(dir);
747}
748
749// Test Adding FFDC from files to a PEL
750TEST_F(PELTest, CreateWithFFDCTest)
751{
752 auto dir = makeTempDir();
753 message::Entry regEntry;
754 uint64_t timestamp = 5;
755
756 regEntry.name = "test";
757 regEntry.subsystem = 5;
758 regEntry.actionFlags = 0xC000;
759 regEntry.src.type = 0xBD;
760 regEntry.src.reasonCode = 0x1234;
761
762 std::vector<std::string> additionalData{"KEY1=VALUE1"};
763 AdditionalData ad{additionalData};
764 NiceMock<MockDataInterface> dataIface;
765 PelFFDC ffdc;
766
767 std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8};
768
769 // This will be trimmed when added
770 std::vector<uint8_t> hugeCustomData(17000, 0x42);
771
772 ffdc.emplace_back(std::move(getJSONFFDC(dir)));
773 ffdc.emplace_back(std::move(getCBORFFDC(dir)));
774 ffdc.emplace_back(std::move(getTextFFDC(dir)));
775 ffdc.emplace_back(std::move(getCustomFFDC(dir, customData)));
776 ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData)));
777
Sumit Kumar9d43a722021-08-24 09:46:19 -0500778 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
779 "system/entry"};
780 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
781 .WillOnce(Return(std::vector<bool>{false, false, false}));
782
Matt Spinler5b289b22020-03-26 14:27:19 -0500783 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
784 ad, ffdc, dataIface};
785
786 EXPECT_TRUE(pel.valid());
787
788 // Clipped to the max
789 EXPECT_EQ(pel.size(), 16384);
790
791 // Check for the FFDC sections
792 size_t udCount = 0;
793 Section* ud = nullptr;
794
795 for (const auto& section : pel.optionalSections())
796 {
797 if (section->header().id == static_cast<uint16_t>(SectionID::userData))
798 {
799 udCount++;
800 ud = section.get();
801 }
802 }
803
804 EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections
805
806 // Check the last section was trimmed to
807 // something a bit less that 17000.
808 EXPECT_GT(ud->header().size, 14000);
809 EXPECT_LT(ud->header().size, 16000);
810
811 fs::remove_all(dir);
812}
Matt Spinler0a90a852020-06-04 13:18:27 -0500813
814// Create a PEL with device callouts
815TEST_F(PELTest, CreateWithDevCalloutsTest)
816{
817 message::Entry regEntry;
818 uint64_t timestamp = 5;
819
820 regEntry.name = "test";
821 regEntry.subsystem = 5;
822 regEntry.actionFlags = 0xC000;
823 regEntry.src.type = 0xBD;
824 regEntry.src.reasonCode = 0x1234;
825
826 NiceMock<MockDataInterface> dataIface;
827 PelFFDC ffdc;
828
829 const auto calloutJSON = R"(
830 {
831 "I2C":
832 {
833 "14":
834 {
835 "114":
836 {
837 "Callouts":[
838 {
839 "Name": "/chassis/motherboard/cpu0",
840 "LocationCode": "P1",
841 "Priority": "H"
842 }
843 ],
844 "Dest": "proc 0 target"
845 }
846 }
847 }
848 })";
849
850 std::vector<std::string> names{"systemA"};
851 EXPECT_CALL(dataIface, getSystemNames)
852 .Times(2)
Matt Spinler1ab66962020-10-29 13:21:44 -0500853 .WillRepeatedly(Return(names));
Matt Spinler0a90a852020-06-04 13:18:27 -0500854
Matt Spinler0d92b522021-06-16 13:28:17 -0600855 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
856 .Times(1)
Matt Spinler0a90a852020-06-04 13:18:27 -0500857 .WillOnce(Return("UXXX-P1"));
858
Matt Spinler2f9225a2020-08-05 12:58:49 -0500859 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler0a90a852020-06-04 13:18:27 -0500860 .WillOnce(
861 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
862
863 EXPECT_CALL(
864 dataIface,
865 getHWCalloutFields(
866 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
867 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
868 SetArgReferee<3>("123456789ABC")));
869
Sumit Kumar9d43a722021-08-24 09:46:19 -0500870 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
871 "system/entry"};
872 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
873 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
874
Matt Spinler0a90a852020-06-04 13:18:27 -0500875 auto dataPath = getPELReadOnlyDataPath();
876 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
877 file << calloutJSON;
878 file.close();
879
880 {
881 std::vector<std::string> data{
882 "CALLOUT_ERRNO=5",
883 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
884 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
885
886 AdditionalData ad{data};
887
888 PEL pel{
889 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
890 ad, ffdc, dataIface};
891
892 ASSERT_TRUE(pel.primarySRC().value()->callouts());
893 auto& callouts = pel.primarySRC().value()->callouts()->callouts();
894 ASSERT_EQ(callouts.size(), 1);
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600895 ASSERT_TRUE(pel.isHwCalloutPresent());
Matt Spinler0a90a852020-06-04 13:18:27 -0500896
897 EXPECT_EQ(callouts[0]->priority(), 'H');
898 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1");
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 const auto& section = pel.optionalSections().back();
906
907 ASSERT_EQ(section->header().id, 0x5544); // UD
908 auto ud = static_cast<UserData*>(section.get());
909
910 // Check that there was a UserData section added that
911 // contains debug details about the device.
912 const auto& d = ud->data();
913 std::string jsonString{d.begin(), d.end()};
914 auto actualJSON = nlohmann::json::parse(jsonString);
915
916 auto expectedJSON = R"(
917 {
918 "PEL Internal Debug Data": {
919 "SRC": [
920 "I2C: bus: 14 address: 114 dest: proc 0 target"
921 ]
922 }
923 }
924 )"_json;
925
926 EXPECT_EQ(actualJSON, expectedJSON);
927 }
928
929 {
930 // Device path not found (wrong i2c addr), so no callouts
931 std::vector<std::string> data{
932 "CALLOUT_ERRNO=5",
933 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
934 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0099"};
935
936 AdditionalData ad{data};
937
938 PEL pel{
939 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
940 ad, ffdc, dataIface};
941
942 // no callouts
943 EXPECT_FALSE(pel.primarySRC().value()->callouts());
944
945 // Now check that there was a UserData section
946 // that contains the lookup error.
947 const auto& section = pel.optionalSections().back();
948
949 ASSERT_EQ(section->header().id, 0x5544); // UD
950 auto ud = static_cast<UserData*>(section.get());
951
952 const auto& d = ud->data();
953
954 std::string jsonString{d.begin(), d.end()};
955
956 auto actualJSON = nlohmann::json::parse(jsonString);
957
958 auto expectedJSON =
959 "{\"PEL Internal Debug Data\":{\"SRC\":"
960 "[\"Problem looking up I2C callouts on 14 153: "
961 "[json.exception.out_of_range.403] key '153' not found\"]}}"_json;
962
963 EXPECT_EQ(actualJSON, expectedJSON);
964 }
965
966 fs::remove_all(dataPath);
967}
Matt Spinlere513dbc2020-08-27 11:14:17 -0500968
969// Test PELs when the callouts are passed in using a JSON file.
970TEST_F(PELTest, CreateWithJSONCalloutsTest)
971{
972 PelFFDCfile ffdcFile;
973 ffdcFile.format = UserDataFormat::json;
974 ffdcFile.subType = 0xCA; // Callout JSON
975 ffdcFile.version = 1;
976
977 // Write these callouts to a JSON file and pass it into
978 // the PEL as an FFDC file.
979 auto inputJSON = R"([
980 {
981 "Priority": "H",
982 "LocationCode": "P0-C1"
983 },
984 {
985 "Priority": "M",
986 "Procedure": "PROCEDURE"
987 }
988 ])"_json;
989
990 auto s = inputJSON.dump();
991 std::vector<uint8_t> data{s.begin(), s.end()};
992 auto dir = makeTempDir();
993 ffdcFile.fd = writeFileAndGetFD(dir, data);
994
995 PelFFDC ffdc;
996 ffdc.push_back(std::move(ffdcFile));
997
998 AdditionalData ad;
999 NiceMock<MockDataInterface> dataIface;
1000
1001 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1002 .Times(1)
1003 .WillOnce(Return("UXXX-P0-C1"));
1004 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1005 .Times(1)
1006 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1007 EXPECT_CALL(dataIface, getHWCalloutFields(
1008 "/inv/system/chassis/motherboard/bmc", _, _, _))
1009 .Times(1)
1010 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1011 SetArgReferee<3>("123456789ABC")));
1012
Sumit Kumar9d43a722021-08-24 09:46:19 -05001013 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1014 "system/entry"};
1015 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1016 .WillOnce(Return(std::vector<bool>{false, false, false}));
1017
Matt Spinlere513dbc2020-08-27 11:14:17 -05001018 message::Entry regEntry;
1019 regEntry.name = "test";
1020 regEntry.subsystem = 5;
1021 regEntry.actionFlags = 0xC000;
1022 regEntry.src.type = 0xBD;
1023 regEntry.src.reasonCode = 0x1234;
1024
1025 PEL pel{regEntry, 42, 5, phosphor::logging::Entry::Level::Error,
1026 ad, ffdc, dataIface};
1027
1028 ASSERT_TRUE(pel.valid());
1029 ASSERT_TRUE(pel.primarySRC().value()->callouts());
1030 const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
1031 ASSERT_EQ(callouts.size(), 2);
Andrew Geisslerf8e750d2022-01-14 14:56:13 -06001032 ASSERT_TRUE(pel.isHwCalloutPresent());
Matt Spinlere513dbc2020-08-27 11:14:17 -05001033
1034 {
1035 EXPECT_EQ(callouts[0]->priority(), 'H');
1036 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1037
1038 auto& fru = callouts[0]->fruIdentity();
1039 EXPECT_EQ(fru->getPN().value(), "1234567");
1040 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1041 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1042 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1043 }
1044 {
1045 EXPECT_EQ(callouts[1]->priority(), 'M');
1046 EXPECT_EQ(callouts[1]->locationCode(), "");
1047
1048 auto& fru = callouts[1]->fruIdentity();
1049 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1050 EXPECT_EQ(fru->failingComponentType(),
1051 src::FRUIdentity::maintenanceProc);
1052 }
1053 fs::remove_all(dir);
1054}
Andrew Geisslerf8e750d2022-01-14 14:56:13 -06001055
1056// Test PELs with symblic FRU callout.
1057TEST_F(PELTest, CreateWithJSONSymblicCalloutTest)
1058{
1059 PelFFDCfile ffdcFile;
1060 ffdcFile.format = UserDataFormat::json;
1061 ffdcFile.subType = 0xCA; // Callout JSON
1062 ffdcFile.version = 1;
1063
1064 // Write these callouts to a JSON file and pass it into
1065 // the PEL as an FFDC file.
1066 auto inputJSON = R"([
1067 {
1068 "Priority": "M",
1069 "Procedure": "SVCDOCS"
1070 }
1071 ])"_json;
1072
1073 auto s = inputJSON.dump();
1074 std::vector<uint8_t> data{s.begin(), s.end()};
1075 auto dir = makeTempDir();
1076 ffdcFile.fd = writeFileAndGetFD(dir, data);
1077
1078 PelFFDC ffdc;
1079 ffdc.push_back(std::move(ffdcFile));
1080
1081 AdditionalData ad;
1082 NiceMock<MockDataInterface> dataIface;
1083
1084 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1085 "system/entry"};
1086 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1087 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1088
1089 message::Entry regEntry;
1090 regEntry.name = "test";
1091 regEntry.subsystem = 5;
1092 regEntry.actionFlags = 0xC000;
1093 regEntry.src.type = 0xBD;
1094 regEntry.src.reasonCode = 0x1234;
1095
1096 PEL pel{regEntry, 42, 5, phosphor::logging::Entry::Level::Error,
1097 ad, ffdc, dataIface};
1098
1099 ASSERT_TRUE(pel.valid());
1100 ASSERT_TRUE(pel.primarySRC().value()->callouts());
1101 const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
1102 ASSERT_EQ(callouts.size(), 1);
1103 ASSERT_FALSE(pel.isHwCalloutPresent());
1104
1105 {
1106 EXPECT_EQ(callouts[0]->priority(), 'M');
1107 EXPECT_EQ(callouts[0]->locationCode(), "");
1108
1109 auto& fru = callouts[0]->fruIdentity();
1110 EXPECT_EQ(fru->getMaintProc().value(), "SVCDOCS");
1111 }
1112 fs::remove_all(dir);
1113}