xref: /openbmc/phosphor-logging/test/openpower-pels/pel_test.cpp (revision 81a91e3ee4bf962111cf555ab9d3c3c51000fa3b)
1 /**
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  */
16 #include "elog_entry.hpp"
17 #include "extensions/openpower-pels/generic.hpp"
18 #include "extensions/openpower-pels/pel.hpp"
19 #include "mocks.hpp"
20 #include "pel_utils.hpp"
21 
22 #include <filesystem>
23 #include <fstream>
24 
25 #include <gtest/gtest.h>
26 
27 namespace fs = std::filesystem;
28 using namespace openpower::pels;
29 using ::testing::NiceMock;
30 using ::testing::Return;
31 
32 class PELTest : public CleanLogID
33 {
34 };
35 
36 fs::path makeTempDir()
37 {
38     char path[] = "/tmp/tempdirXXXXXX";
39     std::filesystem::path dir = mkdtemp(path);
40     return dir;
41 }
42 
43 int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data)
44 {
45     static size_t count = 0;
46     fs::path path = dir / (std::string{"file"} + std::to_string(count));
47     std::ofstream stream{path};
48     count++;
49 
50     stream.write(reinterpret_cast<const char*>(data.data()), data.size());
51     stream.close();
52 
53     FILE* fp = fopen(path.c_str(), "r");
54     return fileno(fp);
55 }
56 
57 TEST_F(PELTest, FlattenTest)
58 {
59     auto data = pelDataFactory(TestPELType::pelSimple);
60     auto pel = std::make_unique<PEL>(data);
61 
62     // Check a few fields
63     EXPECT_TRUE(pel->valid());
64     EXPECT_EQ(pel->id(), 0x80818283);
65     EXPECT_EQ(pel->plid(), 0x50515253);
66     EXPECT_EQ(pel->userHeader().subsystem(), 0x10);
67     EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0);
68 
69     // Test that data in == data out
70     auto flattenedData = pel->data();
71     EXPECT_EQ(data, flattenedData);
72     EXPECT_EQ(flattenedData.size(), pel->size());
73 }
74 
75 TEST_F(PELTest, CommitTimeTest)
76 {
77     auto data = pelDataFactory(TestPELType::pelSimple);
78     auto pel = std::make_unique<PEL>(data);
79 
80     auto origTime = pel->commitTime();
81     pel->setCommitTime();
82     auto newTime = pel->commitTime();
83 
84     EXPECT_NE(origTime, newTime);
85 
86     // Make a new PEL and check new value is still there
87     auto newData = pel->data();
88     auto newPel = std::make_unique<PEL>(newData);
89     EXPECT_EQ(newTime, newPel->commitTime());
90 }
91 
92 TEST_F(PELTest, AssignIDTest)
93 {
94     auto data = pelDataFactory(TestPELType::pelSimple);
95     auto pel = std::make_unique<PEL>(data);
96 
97     auto origID = pel->id();
98     pel->assignID();
99     auto newID = pel->id();
100 
101     EXPECT_NE(origID, newID);
102 
103     // Make a new PEL and check new value is still there
104     auto newData = pel->data();
105     auto newPel = std::make_unique<PEL>(newData);
106     EXPECT_EQ(newID, newPel->id());
107 }
108 
109 TEST_F(PELTest, WithLogIDTest)
110 {
111     auto data = pelDataFactory(TestPELType::pelSimple);
112     auto pel = std::make_unique<PEL>(data, 0x42);
113 
114     EXPECT_TRUE(pel->valid());
115     EXPECT_EQ(pel->obmcLogID(), 0x42);
116 }
117 
118 TEST_F(PELTest, InvalidPELTest)
119 {
120     auto data = pelDataFactory(TestPELType::pelSimple);
121 
122     // Too small
123     data.resize(PrivateHeader::flattenedSize());
124 
125     auto pel = std::make_unique<PEL>(data);
126 
127     EXPECT_TRUE(pel->privateHeader().valid());
128     EXPECT_FALSE(pel->userHeader().valid());
129     EXPECT_FALSE(pel->valid());
130 
131     // Now corrupt the private header
132     data = pelDataFactory(TestPELType::pelSimple);
133     data.at(0) = 0;
134     pel = std::make_unique<PEL>(data);
135 
136     EXPECT_FALSE(pel->privateHeader().valid());
137     EXPECT_TRUE(pel->userHeader().valid());
138     EXPECT_FALSE(pel->valid());
139 }
140 
141 TEST_F(PELTest, EmptyDataTest)
142 {
143     std::vector<uint8_t> data;
144     auto pel = std::make_unique<PEL>(data);
145 
146     EXPECT_FALSE(pel->privateHeader().valid());
147     EXPECT_FALSE(pel->userHeader().valid());
148     EXPECT_FALSE(pel->valid());
149 }
150 
151 TEST_F(PELTest, CreateFromRegistryTest)
152 {
153     message::Entry regEntry;
154     uint64_t timestamp = 5;
155 
156     regEntry.name = "test";
157     regEntry.subsystem = 5;
158     regEntry.actionFlags = 0xC000;
159     regEntry.src.type = 0xBD;
160     regEntry.src.reasonCode = 0x1234;
161 
162     std::vector<std::string> data{"KEY1=VALUE1"};
163     AdditionalData ad{data};
164     NiceMock<MockDataInterface> dataIface;
165     PelFFDC ffdc;
166 
167     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
168             ad,       ffdc, dataIface};
169 
170     EXPECT_TRUE(pel.valid());
171     EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
172     EXPECT_EQ(pel.userHeader().severity(), 0x40);
173 
174     EXPECT_EQ(pel.primarySRC().value()->asciiString(),
175               "BD051234                        ");
176 
177     // Check that certain optional sections have been created
178     size_t mtmsCount = 0;
179     size_t euhCount = 0;
180     size_t udCount = 0;
181 
182     for (const auto& section : pel.optionalSections())
183     {
184         if (section->header().id ==
185             static_cast<uint16_t>(SectionID::failingMTMS))
186         {
187             mtmsCount++;
188         }
189         else if (section->header().id ==
190                  static_cast<uint16_t>(SectionID::extendedUserHeader))
191         {
192             euhCount++;
193         }
194         else if (section->header().id ==
195                  static_cast<uint16_t>(SectionID::userData))
196         {
197             udCount++;
198         }
199     }
200 
201     EXPECT_EQ(mtmsCount, 1);
202     EXPECT_EQ(euhCount, 1);
203     EXPECT_EQ(udCount, 2); // AD section and sysInfo section
204 }
205 
206 // Test that when the AdditionalData size is over 16KB that
207 // the PEL that's created is exactly 16KB since the UserData
208 // section that contains all that data was pruned.
209 TEST_F(PELTest, CreateTooBigADTest)
210 {
211     message::Entry regEntry;
212     uint64_t timestamp = 5;
213 
214     regEntry.name = "test";
215     regEntry.subsystem = 5;
216     regEntry.actionFlags = 0xC000;
217     regEntry.src.type = 0xBD;
218     regEntry.src.reasonCode = 0x1234;
219     PelFFDC ffdc;
220 
221     // Over the 16KB max PEL size
222     std::string bigAD{"KEY1="};
223     bigAD += std::string(17000, 'G');
224 
225     std::vector<std::string> data{bigAD};
226     AdditionalData ad{data};
227     NiceMock<MockDataInterface> dataIface;
228 
229     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
230             ad,       ffdc, dataIface};
231 
232     EXPECT_TRUE(pel.valid());
233     EXPECT_EQ(pel.size(), 16384);
234 
235     // Make sure that there are still 2 UD sections.
236     size_t udCount = 0;
237     for (const auto& section : pel.optionalSections())
238     {
239         if (section->header().id == static_cast<uint16_t>(SectionID::userData))
240         {
241             udCount++;
242         }
243     }
244 
245     EXPECT_EQ(udCount, 2); // AD section and sysInfo section
246 }
247 
248 // Test that we'll create Generic optional sections for sections that
249 // there aren't explicit classes for.
250 TEST_F(PELTest, GenericSectionTest)
251 {
252     auto data = pelDataFactory(TestPELType::pelSimple);
253 
254     std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX'
255                                   0x00, 0x18, // Size
256                                   0x01, 0x02, // version, subtype
257                                   0x03, 0x04, // comp ID
258 
259                                   // some data
260                                   0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63,
261                                   0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A,
262                                   0x00};
263 
264     std::vector<uint8_t> section2{
265         0x59, 0x59, // ID 'YY'
266         0x00, 0x20, // Size
267         0x01, 0x02, // version, subtype
268         0x03, 0x04, // comp ID
269 
270         // some data
271         0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F,
272         0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
273 
274     // Add the new sections at the end
275     data.insert(data.end(), section1.begin(), section1.end());
276     data.insert(data.end(), section2.begin(), section2.end());
277 
278     // Increment the section count
279     data.at(27) += 2;
280     auto origData = data;
281 
282     PEL pel{data};
283 
284     const auto& sections = pel.optionalSections();
285 
286     bool foundXX = false;
287     bool foundYY = false;
288 
289     // Check that we can find these 2 Generic sections
290     for (const auto& section : sections)
291     {
292         if (section->header().id == 0x5858)
293         {
294             foundXX = true;
295             EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
296         }
297         else if (section->header().id == 0x5959)
298         {
299             foundYY = true;
300             EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
301         }
302     }
303 
304     EXPECT_TRUE(foundXX);
305     EXPECT_TRUE(foundYY);
306 
307     // Now flatten and check
308     auto newData = pel.data();
309 
310     EXPECT_EQ(origData, newData);
311 }
312 
313 // Test that an invalid section will still get a Generic object
314 TEST_F(PELTest, InvalidGenericTest)
315 {
316     auto data = pelDataFactory(TestPELType::pelSimple);
317 
318     // Not a valid section
319     std::vector<uint8_t> section1{0x01, 0x02, 0x03};
320 
321     data.insert(data.end(), section1.begin(), section1.end());
322 
323     // Increment the section count
324     data.at(27) += 1;
325 
326     PEL pel{data};
327     EXPECT_FALSE(pel.valid());
328 
329     const auto& sections = pel.optionalSections();
330 
331     bool foundGeneric = false;
332     for (const auto& section : sections)
333     {
334         if (dynamic_cast<Generic*>(section.get()) != nullptr)
335         {
336             foundGeneric = true;
337             EXPECT_EQ(section->valid(), false);
338             break;
339         }
340     }
341 
342     EXPECT_TRUE(foundGeneric);
343 }
344 
345 // Create a UserData section out of AdditionalData
346 TEST_F(PELTest, MakeUDSectionTest)
347 {
348     std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3",
349                                 "ESEL=TEST"};
350     AdditionalData additionalData{ad};
351 
352     auto ud = util::makeADUserDataSection(additionalData);
353 
354     EXPECT_TRUE(ud->valid());
355     EXPECT_EQ(ud->header().id, 0x5544);
356     EXPECT_EQ(ud->header().version, 0x01);
357     EXPECT_EQ(ud->header().subType, 0x01);
358     EXPECT_EQ(ud->header().componentID, 0x2000);
359 
360     const auto& d = ud->data();
361 
362     std::string jsonString{d.begin(), d.end()};
363 
364     std::string expectedJSON =
365         R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
366 
367     // The actual data is null padded to a 4B boundary.
368     std::vector<uint8_t> expectedData;
369     expectedData.resize(52, '\0');
370     memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size());
371 
372     EXPECT_EQ(d, expectedData);
373 
374     // Ensure we can read this as JSON
375     auto newJSON = nlohmann::json::parse(jsonString);
376     EXPECT_EQ(newJSON["KEY1"], "VALUE1");
377     EXPECT_EQ(newJSON["KEY2"], "VALUE2");
378     EXPECT_EQ(newJSON["KEY3"], "VALUE3");
379 }
380 
381 // Create the UserData section that contains system info
382 TEST_F(PELTest, SysInfoSectionTest)
383 {
384     MockDataInterface dataIface;
385 
386     EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
387     EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready"));
388     EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On"));
389     EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off"));
390 
391     std::string pid = "_PID=" + std::to_string(getpid());
392     std::vector<std::string> ad{pid};
393     AdditionalData additionalData{ad};
394 
395     auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
396 
397     EXPECT_TRUE(ud->valid());
398     EXPECT_EQ(ud->header().id, 0x5544);
399     EXPECT_EQ(ud->header().version, 0x01);
400     EXPECT_EQ(ud->header().subType, 0x01);
401     EXPECT_EQ(ud->header().componentID, 0x2000);
402 
403     // Pull out the JSON data and check it.
404     const auto& d = ud->data();
405     std::string jsonString{d.begin(), d.end()};
406     auto json = nlohmann::json::parse(jsonString);
407 
408     // Ensure the 'Process Name' entry contains 'pel_test'
409     auto name = json["Process Name"].get<std::string>();
410     EXPECT_NE(name.find("pel_test"), std::string::npos);
411 
412     auto version = json["BMC Version ID"].get<std::string>();
413     EXPECT_EQ(version, "ABCD1234");
414 
415     auto state = json["BMCState"].get<std::string>();
416     EXPECT_EQ(state, "Ready");
417 
418     state = json["ChassisState"].get<std::string>();
419     EXPECT_EQ(state, "On");
420 
421     state = json["HostState"].get<std::string>();
422     EXPECT_EQ(state, "Off");
423 }
424 
425 // Test that the sections that override
426 //     virtual std::optional<std::string> Section::getJSON() const
427 // return valid JSON.
428 TEST_F(PELTest, SectionJSONTest)
429 {
430     auto data = pelDataFactory(TestPELType::pelSimple);
431     PEL pel{data};
432 
433     // Check that all JSON returned from the sections is
434     // parseable by nlohmann::json, which will throw an
435     // exception and fail the test if there is a problem.
436 
437     // The getJSON() response needs to be wrapped in a { } to make
438     // actual valid JSON (PEL::toJSON() usually handles that).
439 
440     auto jsonString = pel.privateHeader().getJSON();
441 
442     // PrivateHeader always prints JSON
443     ASSERT_TRUE(jsonString);
444     *jsonString = '{' + *jsonString + '}';
445     auto json = nlohmann::json::parse(*jsonString);
446 
447     jsonString = pel.userHeader().getJSON();
448 
449     // UserHeader always prints JSON
450     ASSERT_TRUE(jsonString);
451     *jsonString = '{' + *jsonString + '}';
452     json = nlohmann::json::parse(*jsonString);
453 
454     for (const auto& section : pel.optionalSections())
455     {
456         // The optional sections may or may not have implemented getJSON().
457         jsonString = section->getJSON();
458         if (jsonString)
459         {
460             *jsonString = '{' + *jsonString + '}';
461             auto json = nlohmann::json::parse(*jsonString);
462         }
463     }
464 }
465 
466 PelFFDCfile getJSONFFDC(const fs::path& dir)
467 {
468     PelFFDCfile ffdc;
469     ffdc.format = UserDataFormat::json;
470     ffdc.subType = 5;
471     ffdc.version = 42;
472 
473     auto inputJSON = R"({
474         "key1": "value1",
475         "key2": 42,
476         "key3" : [1, 2, 3, 4, 5],
477         "key4": {"key5": "value5"}
478     })"_json;
479 
480     // Write the JSON to a file and get its descriptor.
481     auto s = inputJSON.dump();
482     std::vector<uint8_t> data{s.begin(), s.end()};
483     ffdc.fd = writeFileAndGetFD(dir, data);
484 
485     return ffdc;
486 }
487 
488 TEST_F(PELTest, MakeJSONFileUDSectionTest)
489 {
490     auto dir = makeTempDir();
491 
492     {
493         auto ffdc = getJSONFFDC(dir);
494 
495         auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
496         close(ffdc.fd);
497         ASSERT_TRUE(ud);
498         ASSERT_TRUE(ud->valid());
499         EXPECT_EQ(ud->header().id, 0x5544);
500 
501         EXPECT_EQ(ud->header().version,
502                   static_cast<uint8_t>(UserDataFormatVersion::json));
503         EXPECT_EQ(ud->header().subType,
504                   static_cast<uint8_t>(UserDataFormat::json));
505         EXPECT_EQ(ud->header().componentID,
506                   static_cast<uint16_t>(ComponentID::phosphorLogging));
507 
508         // Pull the JSON back out of the the UserData section
509         const auto& d = ud->data();
510         std::string js{d.begin(), d.end()};
511         auto json = nlohmann::json::parse(js);
512 
513         EXPECT_EQ("value1", json["key1"].get<std::string>());
514         EXPECT_EQ(42, json["key2"].get<int>());
515 
516         std::vector<int> key3Values{1, 2, 3, 4, 5};
517         EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
518 
519         std::map<std::string, std::string> key4Values{{"key5", "value5"}};
520         auto actual = json["key4"].get<std::map<std::string, std::string>>();
521         EXPECT_EQ(key4Values, actual);
522     }
523 
524     {
525         // A bad FD
526         PelFFDCfile ffdc;
527         ffdc.format = UserDataFormat::json;
528         ffdc.subType = 5;
529         ffdc.version = 42;
530         ffdc.fd = 10000;
531 
532         // The section shouldn't get made
533         auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
534         ASSERT_FALSE(ud);
535     }
536 
537     fs::remove_all(dir);
538 }
539 
540 PelFFDCfile getCBORFFDC(const fs::path& dir)
541 {
542     PelFFDCfile ffdc;
543     ffdc.format = UserDataFormat::cbor;
544     ffdc.subType = 5;
545     ffdc.version = 42;
546 
547     auto inputJSON = R"({
548         "key1": "value1",
549         "key2": 42,
550         "key3" : [1, 2, 3, 4, 5],
551         "key4": {"key5": "value5"}
552     })"_json;
553 
554     // Convert the JSON to CBOR and write it to a file
555     auto data = nlohmann::json::to_cbor(inputJSON);
556     ffdc.fd = writeFileAndGetFD(dir, data);
557 
558     return ffdc;
559 }
560 
561 TEST_F(PELTest, MakeCBORFileUDSectionTest)
562 {
563     auto dir = makeTempDir();
564 
565     auto ffdc = getCBORFFDC(dir);
566     auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
567     close(ffdc.fd);
568     ASSERT_TRUE(ud);
569     ASSERT_TRUE(ud->valid());
570     EXPECT_EQ(ud->header().id, 0x5544);
571 
572     EXPECT_EQ(ud->header().version,
573               static_cast<uint8_t>(UserDataFormatVersion::cbor));
574     EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor));
575     EXPECT_EQ(ud->header().componentID,
576               static_cast<uint16_t>(ComponentID::phosphorLogging));
577 
578     // Pull the CBOR back out of the PEL section
579     // The number of pad bytes to make the section be 4B aligned
580     // was added at the end, read it and then remove it and the
581     // padding before parsing it.
582     auto data = ud->data();
583     Stream stream{data};
584     stream.offset(data.size() - 4);
585     uint32_t pad;
586     stream >> pad;
587 
588     data.resize(data.size() - 4 - pad);
589 
590     auto json = nlohmann::json::from_cbor(data);
591 
592     EXPECT_EQ("value1", json["key1"].get<std::string>());
593     EXPECT_EQ(42, json["key2"].get<int>());
594 
595     std::vector<int> key3Values{1, 2, 3, 4, 5};
596     EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
597 
598     std::map<std::string, std::string> key4Values{{"key5", "value5"}};
599     auto actual = json["key4"].get<std::map<std::string, std::string>>();
600     EXPECT_EQ(key4Values, actual);
601 
602     fs::remove_all(dir);
603 }
604 
605 PelFFDCfile getTextFFDC(const fs::path& dir)
606 {
607     PelFFDCfile ffdc;
608     ffdc.format = UserDataFormat::text;
609     ffdc.subType = 5;
610     ffdc.version = 42;
611 
612     std::string text{"this is some text that will be used for FFDC"};
613     std::vector<uint8_t> data{text.begin(), text.end()};
614 
615     ffdc.fd = writeFileAndGetFD(dir, data);
616 
617     return ffdc;
618 }
619 
620 TEST_F(PELTest, MakeTextFileUDSectionTest)
621 {
622     auto dir = makeTempDir();
623 
624     auto ffdc = getTextFFDC(dir);
625     auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
626     close(ffdc.fd);
627     ASSERT_TRUE(ud);
628     ASSERT_TRUE(ud->valid());
629     EXPECT_EQ(ud->header().id, 0x5544);
630 
631     EXPECT_EQ(ud->header().version,
632               static_cast<uint8_t>(UserDataFormatVersion::text));
633     EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text));
634     EXPECT_EQ(ud->header().componentID,
635               static_cast<uint16_t>(ComponentID::phosphorLogging));
636 
637     // Get the text back out
638     std::string text{ud->data().begin(), ud->data().end()};
639     EXPECT_EQ(text, "this is some text that will be used for FFDC");
640 
641     fs::remove_all(dir);
642 }
643 
644 PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data)
645 {
646     PelFFDCfile ffdc;
647     ffdc.format = UserDataFormat::custom;
648     ffdc.subType = 5;
649     ffdc.version = 42;
650 
651     ffdc.fd = writeFileAndGetFD(dir, data);
652 
653     return ffdc;
654 }
655 
656 TEST_F(PELTest, MakeCustomFileUDSectionTest)
657 {
658     auto dir = makeTempDir();
659 
660     {
661         std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8};
662 
663         auto ffdc = getCustomFFDC(dir, data);
664         auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
665         close(ffdc.fd);
666         ASSERT_TRUE(ud);
667         ASSERT_TRUE(ud->valid());
668         EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size
669         EXPECT_EQ(ud->header().id, 0x5544);
670 
671         EXPECT_EQ(ud->header().version, 42);
672         EXPECT_EQ(ud->header().subType, 5);
673         EXPECT_EQ(ud->header().componentID, 0x2002);
674 
675         // Get the data back out
676         std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
677         EXPECT_EQ(data, newData);
678     }
679 
680     // Do the same thing again, but make it be non 4B aligned
681     // so the data gets padded.
682     {
683         std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
684 
685         auto ffdc = getCustomFFDC(dir, data);
686         auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
687         close(ffdc.fd);
688         ASSERT_TRUE(ud);
689         ASSERT_TRUE(ud->valid());
690         EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size
691         EXPECT_EQ(ud->header().id, 0x5544);
692 
693         EXPECT_EQ(ud->header().version, 42);
694         EXPECT_EQ(ud->header().subType, 5);
695         EXPECT_EQ(ud->header().componentID, 0x2002);
696 
697         // Get the data back out
698         std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
699 
700         // pad the original to 12B so we can compare
701         data.push_back(0);
702         data.push_back(0);
703         data.push_back(0);
704 
705         EXPECT_EQ(data, newData);
706     }
707 
708     fs::remove_all(dir);
709 }
710 
711 // Test Adding FFDC from files to a PEL
712 TEST_F(PELTest, CreateWithFFDCTest)
713 {
714     auto dir = makeTempDir();
715     message::Entry regEntry;
716     uint64_t timestamp = 5;
717 
718     regEntry.name = "test";
719     regEntry.subsystem = 5;
720     regEntry.actionFlags = 0xC000;
721     regEntry.src.type = 0xBD;
722     regEntry.src.reasonCode = 0x1234;
723 
724     std::vector<std::string> additionalData{"KEY1=VALUE1"};
725     AdditionalData ad{additionalData};
726     NiceMock<MockDataInterface> dataIface;
727     PelFFDC ffdc;
728 
729     std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8};
730 
731     // This will be trimmed when added
732     std::vector<uint8_t> hugeCustomData(17000, 0x42);
733 
734     ffdc.emplace_back(std::move(getJSONFFDC(dir)));
735     ffdc.emplace_back(std::move(getCBORFFDC(dir)));
736     ffdc.emplace_back(std::move(getTextFFDC(dir)));
737     ffdc.emplace_back(std::move(getCustomFFDC(dir, customData)));
738     ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData)));
739 
740     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
741             ad,       ffdc, dataIface};
742 
743     EXPECT_TRUE(pel.valid());
744 
745     // Clipped to the max
746     EXPECT_EQ(pel.size(), 16384);
747 
748     // Check for the FFDC sections
749     size_t udCount = 0;
750     Section* ud = nullptr;
751 
752     for (const auto& section : pel.optionalSections())
753     {
754         if (section->header().id == static_cast<uint16_t>(SectionID::userData))
755         {
756             udCount++;
757             ud = section.get();
758         }
759     }
760 
761     EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections
762 
763     // Check the last section was trimmed to
764     // something a bit less that 17000.
765     EXPECT_GT(ud->header().size, 14000);
766     EXPECT_LT(ud->header().size, 16000);
767 
768     fs::remove_all(dir);
769 }
770