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 "extensions/openpower-pels/paths.hpp"
17 #include "extensions/openpower-pels/repository.hpp"
18 #include "pel_utils.hpp"
19 
20 #include <ext/stdio_filebuf.h>
21 
22 #include <filesystem>
23 
24 #include <gtest/gtest.h>
25 
26 using namespace openpower::pels;
27 namespace fs = std::filesystem;
28 
29 /**
30  * Clean the Repo after every testcase.
31  * And because we have PEL object, also clean up
32  * the log ID.
33  */
34 class RepositoryTest : public CleanLogID
35 {
36   protected:
37     void SetUp() override
38     {
39         repoPath = getPELRepoPath();
40     }
41 
42     void TearDown() override
43     {
44         fs::remove_all(repoPath);
45     }
46 
47     fs::path repoPath;
48 };
49 
50 TEST_F(RepositoryTest, FilenameTest)
51 {
52     BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8};
53 
54     EXPECT_EQ(Repository::getPELFilename(0x12345678, date),
55               "2030112813060708_12345678");
56 
57     EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date),
58               "2030112813060708_AABBCCDD");
59 
60     EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date),
61               "2030112813060708_0003AFF1");
62 
63     EXPECT_EQ(Repository::getPELFilename(100, date),
64               "2030112813060708_00000064");
65 
66     EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000");
67 }
68 
69 TEST_F(RepositoryTest, AddTest)
70 {
71     Repository repo{repoPath};
72     auto data = pelDataFactory(TestPELType::pelSimple);
73     auto pel = std::make_unique<PEL>(data);
74 
75     repo.add(pel);
76 
77     // Check that the PEL was stored where it was supposed to be,
78     // and that it wrote the PEL data.
79     const auto ts = pel->privateHeader().commitTimestamp();
80     auto name = Repository::getPELFilename(pel->id(), ts);
81 
82     fs::path file = repoPath / "logs" / name;
83     EXPECT_TRUE(fs::exists(file));
84 
85     auto newData = readPELFile(file);
86     auto pelData = pel->data();
87     EXPECT_EQ(*newData, pelData);
88 
89     EXPECT_EQ(repo.lastPelID(), pel->id());
90 }
91 
92 TEST_F(RepositoryTest, RemoveTest)
93 {
94     using pelID = Repository::LogID::Pel;
95     using obmcID = Repository::LogID::Obmc;
96 
97     // Add and remove a PEL from the repo
98 
99     Repository repo{repoPath};
100 
101     auto data = pelDataFactory(TestPELType::pelSimple);
102     auto pel = std::make_unique<PEL>(data, 1);
103 
104     pel->assignID();
105     Repository::LogID id{pelID{pel->id()}, obmcID{pel->obmcLogID()}};
106 
107     repo.add(pel);
108 
109     auto removedID = repo.remove(id);
110     ASSERT_TRUE(removedID);
111     EXPECT_EQ(*removedID, id);
112 
113     EXPECT_FALSE(repo.hasPEL(id));
114 
115     // Try to remove it again, not there
116     EXPECT_FALSE(repo.remove(id));
117 }
118 
119 TEST_F(RepositoryTest, RestoreTest)
120 {
121     using pelID = Repository::LogID::Pel;
122     using obmcID = Repository::LogID::Obmc;
123 
124     std::vector<Repository::LogID> ids;
125 
126     {
127         Repository repo{repoPath};
128 
129         // Add some PELs to the repository
130         {
131             auto data = pelDataFactory(TestPELType::pelSimple);
132             auto pel = std::make_unique<PEL>(data, 1);
133             pel->assignID();
134             repo.add(pel);
135             ids.emplace_back(pelID(pel->id()), obmcID(1));
136         }
137         {
138             auto data = pelDataFactory(TestPELType::pelSimple);
139             auto pel = std::make_unique<PEL>(data, 2);
140             pel->assignID();
141             repo.add(pel);
142             ids.emplace_back(pelID(pel->id()), obmcID(2));
143         }
144 
145         // Check they're there
146         EXPECT_TRUE(repo.hasPEL(ids[0]));
147         EXPECT_TRUE(repo.hasPEL(ids[1]));
148 
149         // Do some other search tests while we're here.
150 
151         // Search based on PEL ID
152         Repository::LogID id(pelID(ids[0].pelID));
153         EXPECT_TRUE(repo.hasPEL(id));
154 
155         // Search based on OBMC log ID
156         id.pelID.id = 0;
157         id.obmcID = ids[0].obmcID;
158         EXPECT_TRUE(repo.hasPEL(id));
159 
160         // ... based on the other PEL ID
161         id.pelID = ids[1].pelID;
162         id.obmcID.id = 0;
163         EXPECT_TRUE(repo.hasPEL(id));
164 
165         // Not found
166         id.pelID.id = 99;
167         id.obmcID.id = 100;
168         EXPECT_FALSE(repo.hasPEL(id));
169 
170         // Try to remove it anyway
171         EXPECT_FALSE(repo.remove(id));
172     }
173 
174     {
175         // Restore and check they're still there, then
176         // remove them.
177         Repository repo{repoPath};
178         EXPECT_TRUE(repo.hasPEL(ids[0]));
179         EXPECT_TRUE(repo.hasPEL(ids[1]));
180 
181         repo.remove(ids[0]);
182         EXPECT_FALSE(repo.hasPEL(ids[0]));
183 
184         repo.remove(ids[1]);
185         EXPECT_FALSE(repo.hasPEL(ids[1]));
186     }
187 }
188 
189 TEST_F(RepositoryTest, TestGetPELData)
190 {
191     using ID = Repository::LogID;
192     Repository repo{repoPath};
193 
194     ID badID{ID::Pel(42)};
195     auto noData = repo.getPELData(badID);
196     EXPECT_FALSE(noData);
197 
198     // Add a PEL to the repo, and get the data back with getPELData.
199     auto data = pelDataFactory(TestPELType::pelSimple);
200     auto dataCopy = data;
201     auto pel = std::make_unique<PEL>(data);
202     auto pelID = pel->id();
203     repo.add(pel);
204 
205     ID id{ID::Pel(pelID)};
206     auto pelData = repo.getPELData(id);
207 
208     ASSERT_TRUE(pelData);
209     EXPECT_EQ(dataCopy, *pelData);
210 }
211 
212 TEST_F(RepositoryTest, TestForEach)
213 {
214     Repository repo{repoPath};
215 
216     // Add 2 PELs
217     auto data = pelDataFactory(TestPELType::pelSimple);
218     auto pel = std::make_unique<PEL>(data);
219     repo.add(pel);
220 
221     pel = std::make_unique<PEL>(data);
222     pel->assignID();
223     pel->setCommitTime();
224     repo.add(pel);
225 
226     // Make a function that saves the IDs
227     std::vector<uint32_t> ids;
228     Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
229         ids.push_back(pel.id());
230         return false;
231     };
232 
233     repo.for_each(f1);
234 
235     EXPECT_EQ(ids.size(), 2);
236 
237     // Stop after the first time in.
238     Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
239         ids.push_back(pel.id());
240         return true;
241     };
242 
243     ids.clear();
244     repo.for_each(f2);
245     EXPECT_EQ(ids.size(), 1);
246 }
247 
248 TEST_F(RepositoryTest, TestSubscriptions)
249 {
250     std::vector<uint32_t> added;
251     std::vector<uint32_t> removed;
252 
253     Repository::AddCallback ac = [&added](const PEL& pel) {
254         added.push_back(pel.id());
255     };
256 
257     Repository::DeleteCallback dc = [&removed](uint32_t id) {
258         removed.push_back(id);
259     };
260 
261     Repository repo{repoPath};
262     repo.subscribeToAdds("test", ac);
263     repo.subscribeToDeletes("test", dc);
264 
265     auto data = pelDataFactory(TestPELType::pelSimple);
266     auto pel = std::make_unique<PEL>(data);
267     auto pelID = pel->id();
268     repo.add(pel);
269 
270     EXPECT_EQ(added.size(), 1);
271 
272     using ID = Repository::LogID;
273     ID id{ID::Pel(pelID)};
274     repo.remove(id);
275 
276     EXPECT_EQ(removed.size(), 1);
277 
278     repo.unsubscribeFromAdds("test");
279     repo.unsubscribeFromDeletes("test");
280 
281     added.clear();
282     removed.clear();
283 
284     repo.add(pel);
285     EXPECT_EQ(added.size(), 0);
286 
287     repo.remove(id);
288     EXPECT_EQ(removed.size(), 0);
289 }
290 
291 TEST_F(RepositoryTest, TestGetAttributes)
292 {
293     uint32_t pelID = 0;
294     std::bitset<16> actionFlags;
295 
296     {
297         Repository repo{repoPath};
298 
299         // Add a PEL to the repo
300         auto data = pelDataFactory(TestPELType::pelSimple);
301         auto pel = std::make_unique<PEL>(data);
302         repo.add(pel);
303 
304         pelID = pel->id();
305         actionFlags = pel->userHeader().actionFlags();
306 
307         using ID = Repository::LogID;
308         ID id{ID::Pel(pelID)};
309 
310         auto a = repo.getPELAttributes(id);
311         EXPECT_TRUE(a);
312         EXPECT_EQ((*a).get().actionFlags, actionFlags);
313 
314         id.pelID.id = 0;
315         a = repo.getPELAttributes(id);
316         EXPECT_FALSE(a);
317     }
318 
319     {
320         // Restore the repository and check again
321         Repository repo{repoPath};
322 
323         using ID = Repository::LogID;
324         ID id{ID::Pel(pelID)};
325 
326         auto a = repo.getPELAttributes(id);
327         EXPECT_TRUE(a);
328         EXPECT_EQ((*a).get().actionFlags, actionFlags);
329 
330         id.pelID.id = 0;
331         a = repo.getPELAttributes(id);
332         EXPECT_FALSE(a);
333     }
334 }
335 
336 TEST_F(RepositoryTest, TestSetHostState)
337 {
338     // Add a PEL to the repo
339     auto data = pelDataFactory(TestPELType::pelSimple);
340     auto pel = std::make_unique<PEL>(data);
341     using ID = Repository::LogID;
342     ID id{ID::Pel(pel->id())};
343 
344     {
345         Repository repo{repoPath};
346 
347         repo.add(pel);
348 
349         auto a = repo.getPELAttributes(id);
350         EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL);
351 
352         repo.setPELHostTransState(pel->id(), TransmissionState::acked);
353 
354         // First, check the attributes
355         a = repo.getPELAttributes(id);
356         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
357 
358         // Next, check the PEL data itself
359         auto pelData = repo.getPELData(id);
360         PEL newPEL{*pelData};
361         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
362     }
363 
364     {
365         // Now restore, and check again
366         Repository repo{repoPath};
367 
368         // First, check the attributes
369         auto a = repo.getPELAttributes(id);
370         EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
371 
372         // Next, check the PEL data itself
373         auto pelData = repo.getPELData(id);
374         PEL newPEL{*pelData};
375         EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
376     }
377 }
378 
379 TEST_F(RepositoryTest, TestSetHMCState)
380 {
381     // Add a PEL to the repo
382     auto data = pelDataFactory(TestPELType::pelSimple);
383     auto pel = std::make_unique<PEL>(data);
384     using ID = Repository::LogID;
385     ID id{ID::Pel(pel->id())};
386 
387     {
388         Repository repo{repoPath};
389 
390         repo.add(pel);
391 
392         auto a = repo.getPELAttributes(id);
393         EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL);
394 
395         repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
396 
397         // First, check the attributes
398         a = repo.getPELAttributes(id);
399         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
400 
401         // Next, check the PEL data itself
402         auto pelData = repo.getPELData(id);
403         PEL newPEL{*pelData};
404         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
405     }
406 
407     {
408         // Now restore, and check again
409         Repository repo{repoPath};
410 
411         // First, check the attributes
412         auto a = repo.getPELAttributes(id);
413         EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
414 
415         // Next, check the PEL data itself
416         auto pelData = repo.getPELData(id);
417         PEL newPEL{*pelData};
418         EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
419     }
420 }
421 
422 TEST_F(RepositoryTest, TestGetPELFD)
423 {
424     Repository repo{repoPath};
425 
426     auto data = pelDataFactory(TestPELType::pelSimple);
427     auto pel = std::make_unique<PEL>(data);
428     pel->setCommitTime();
429     pel->assignID();
430 
431     repo.add(pel);
432 
433     using ID = Repository::LogID;
434     ID id{ID::Pel(pel->id())};
435 
436     auto fd = repo.getPELFD(id);
437 
438     EXPECT_TRUE(fd);
439 
440     // Get the size
441     struct stat s;
442     int r = fstat(*fd, &s);
443     ASSERT_EQ(r, 0);
444 
445     auto size = s.st_size;
446 
447     // Read the PEL data out of the FD
448     FILE* fp = fdopen(*fd, "r");
449     ASSERT_NE(fp, nullptr);
450 
451     std::vector<uint8_t> newData;
452     newData.resize(size);
453     r = fread(newData.data(), 1, size, fp);
454     EXPECT_EQ(r, size);
455 
456     PEL newPEL{newData};
457 
458     EXPECT_TRUE(newPEL.valid());
459     EXPECT_EQ(newPEL.id(), pel->id());
460 
461     fclose(fp);
462 
463     // Call getPELFD again, this time with a bad ID
464     id.pelID.id = 42;
465     fd = repo.getPELFD(id);
466 
467     EXPECT_FALSE(fd);
468 }
469 
470 // Test the repo size statistics
471 TEST_F(RepositoryTest, TestRepoSizes)
472 {
473     uint32_t id = 1;
474 
475     Repository repo{repoPath, 10000, 500};
476 
477     // All of the size stats are the sizes on disk a PEL takes up,
478     // which is different than the file size.  Disk usage seems
479     // to have a granularity of 4096 bytes.  This probably shouldn't
480     // be hardcoded, but I don't know how to look it up dynamically.
481 
482     // All sizes are zero
483     {
484         const auto& stats = repo.getSizeStats();
485         EXPECT_EQ(stats.total, 0);
486         EXPECT_EQ(stats.bmc, 0);
487         EXPECT_EQ(stats.nonBMC, 0);
488         EXPECT_EQ(stats.bmcServiceable, 0);
489         EXPECT_EQ(stats.bmcInfo, 0);
490         EXPECT_EQ(stats.nonBMCServiceable, 0);
491         EXPECT_EQ(stats.nonBMCInfo, 0);
492     }
493 
494     // Add a 2000B BMC predictive error
495     auto data = pelFactory(id++, 'O', 0x20, 0x8800, 2000);
496     auto pel = std::make_unique<PEL>(data);
497     auto pelID1 = pel->id();
498     repo.add(pel);
499 
500     {
501         const auto& stats = repo.getSizeStats();
502         EXPECT_EQ(stats.total, 4096);
503         EXPECT_EQ(stats.bmc, 4096);
504         EXPECT_EQ(stats.nonBMC, 0);
505         EXPECT_EQ(stats.bmcServiceable, 4096);
506         EXPECT_EQ(stats.bmcInfo, 0);
507         EXPECT_EQ(stats.nonBMCServiceable, 0);
508         EXPECT_EQ(stats.nonBMCInfo, 0);
509     }
510 
511     // Add a 5000B BMC informational error
512     data = pelFactory(id++, 'O', 0x00, 0x8800, 5000);
513     pel = std::make_unique<PEL>(data);
514     auto pelID2 = pel->id();
515     repo.add(pel);
516 
517     {
518         const auto& stats = repo.getSizeStats();
519         EXPECT_EQ(stats.total, 4096 + 8192);
520         EXPECT_EQ(stats.bmc, 4096 + 8192);
521         EXPECT_EQ(stats.nonBMC, 0);
522         EXPECT_EQ(stats.bmcServiceable, 4096);
523         EXPECT_EQ(stats.bmcInfo, 8192);
524         EXPECT_EQ(stats.nonBMCServiceable, 0);
525         EXPECT_EQ(stats.nonBMCInfo, 0);
526     }
527 
528     // Add a 4000B Hostboot unrecoverable error
529     data = pelFactory(id++, 'B', 0x40, 0x8800, 4000);
530     pel = std::make_unique<PEL>(data);
531     auto pelID3 = pel->id();
532     repo.add(pel);
533 
534     {
535         const auto& stats = repo.getSizeStats();
536         EXPECT_EQ(stats.total, 4096 + 8192 + 4096);
537         EXPECT_EQ(stats.bmc, 4096 + 8192);
538         EXPECT_EQ(stats.nonBMC, 4096);
539         EXPECT_EQ(stats.bmcServiceable, 4096);
540         EXPECT_EQ(stats.bmcInfo, 8192);
541         EXPECT_EQ(stats.nonBMCServiceable, 4096);
542         EXPECT_EQ(stats.nonBMCInfo, 0);
543     }
544 
545     // Add a 5000B Hostboot informational error
546     data = pelFactory(id++, 'B', 0x00, 0x8800, 5000);
547     pel = std::make_unique<PEL>(data);
548     auto pelID4 = pel->id();
549     repo.add(pel);
550 
551     {
552         const auto& stats = repo.getSizeStats();
553         EXPECT_EQ(stats.total, 4096 + 8192 + 4096 + 8192);
554         EXPECT_EQ(stats.bmc, 4096 + 8192);
555         EXPECT_EQ(stats.nonBMC, 4096 + 8192);
556         EXPECT_EQ(stats.bmcServiceable, 4096);
557         EXPECT_EQ(stats.bmcInfo, 8192);
558         EXPECT_EQ(stats.nonBMCServiceable, 4096);
559         EXPECT_EQ(stats.nonBMCInfo, 8192);
560     }
561 
562     // Remove the BMC serviceable error
563     using ID = Repository::LogID;
564     ID id1{ID::Pel(pelID1)};
565 
566     repo.remove(id1);
567     {
568         const auto& stats = repo.getSizeStats();
569         EXPECT_EQ(stats.total, 8192 + 4096 + 8192);
570         EXPECT_EQ(stats.bmc, 8192);
571         EXPECT_EQ(stats.nonBMC, 4096 + 8192);
572         EXPECT_EQ(stats.bmcServiceable, 0);
573         EXPECT_EQ(stats.bmcInfo, 8192);
574         EXPECT_EQ(stats.nonBMCServiceable, 4096);
575         EXPECT_EQ(stats.nonBMCInfo, 8192);
576     }
577 
578     // Remove the Hostboot informational error
579     ID id4{ID::Pel(pelID4)};
580 
581     repo.remove(id4);
582     {
583         const auto& stats = repo.getSizeStats();
584         EXPECT_EQ(stats.total, 8192 + 4096);
585         EXPECT_EQ(stats.bmc, 8192);
586         EXPECT_EQ(stats.nonBMC, 4096);
587         EXPECT_EQ(stats.bmcServiceable, 0);
588         EXPECT_EQ(stats.bmcInfo, 8192);
589         EXPECT_EQ(stats.nonBMCServiceable, 4096);
590         EXPECT_EQ(stats.nonBMCInfo, 0);
591     }
592 
593     // Remove the BMC informational error
594     ID id2{ID::Pel(pelID2)};
595 
596     repo.remove(id2);
597     {
598         const auto& stats = repo.getSizeStats();
599         EXPECT_EQ(stats.total, 4096);
600         EXPECT_EQ(stats.bmc, 0);
601         EXPECT_EQ(stats.nonBMC, 4096);
602         EXPECT_EQ(stats.bmcServiceable, 0);
603         EXPECT_EQ(stats.bmcInfo, 0);
604         EXPECT_EQ(stats.nonBMCServiceable, 4096);
605         EXPECT_EQ(stats.nonBMCInfo, 0);
606     }
607 
608     // Remove the hostboot unrecoverable error
609     ID id3{ID::Pel(pelID3)};
610 
611     repo.remove(id3);
612     {
613         const auto& stats = repo.getSizeStats();
614         EXPECT_EQ(stats.total, 0);
615         EXPECT_EQ(stats.bmc, 0);
616         EXPECT_EQ(stats.nonBMC, 0);
617         EXPECT_EQ(stats.bmcServiceable, 0);
618         EXPECT_EQ(stats.bmcInfo, 0);
619         EXPECT_EQ(stats.nonBMCServiceable, 0);
620         EXPECT_EQ(stats.nonBMCInfo, 0);
621     }
622 }
623 
624 // Prune PELs, when no HMC/OS/PHYP acks
625 TEST_F(RepositoryTest, TestPruneNoAcks)
626 {
627     Repository repo{repoPath, 4096 * 20, 100};
628 
629     // Add 10 4096B (on disk) PELs of BMC nonInfo, Info and nonBMC info,
630     // nonInfo errors. None of them acked by PHYP, host, or HMC.
631     for (uint32_t i = 1; i <= 10; i++)
632     {
633         // BMC predictive
634         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
635         auto pel = std::make_unique<PEL>(data);
636         repo.add(pel);
637 
638         // BMC info
639         data = pelFactory(i + 100, 'O', 0x0, 0x8800, 500);
640         pel = std::make_unique<PEL>(data);
641         repo.add(pel);
642 
643         // Hostboot predictive
644         data = pelFactory(i + 200, 'B', 0x20, 0x8800, 500);
645         pel = std::make_unique<PEL>(data);
646         repo.add(pel);
647 
648         // Hostboot info
649         data = pelFactory(i + 300, 'B', 0x0, 0x8800, 500);
650         pel = std::make_unique<PEL>(data);
651         repo.add(pel);
652     }
653 
654     const auto& sizes = repo.getSizeStats();
655     EXPECT_EQ(sizes.total, 4096 * 40);
656 
657     // Sanity check the very first PELs with IDs 1 to 4 are
658     // there so we can check they are removed after the prune.
659     for (uint32_t i = 1; i < 5; i++)
660     {
661         Repository::LogID id{Repository::LogID::Pel{i}};
662         EXPECT_TRUE(repo.getPELAttributes(id));
663     }
664 
665     // Prune down to 15%/30%/15%/30% = 90% total
666     auto IDs = repo.prune();
667 
668     // Check the final sizes
669     EXPECT_EQ(sizes.total, 4096 * 18);            // 90% of 20 PELs
670     EXPECT_EQ(sizes.bmcInfo, 4096 * 3);           // 15% of 20 PELs
671     EXPECT_EQ(sizes.bmcServiceable, 4096 * 6);    // 30% of 20 PELs
672     EXPECT_EQ(sizes.nonBMCInfo, 4096 * 3);        // 15% of 20 PELs
673     EXPECT_EQ(sizes.nonBMCServiceable, 4096 * 6); // 30% of 20 PELs
674 
675     // Check that at least the 4 oldest, which are the oldest of
676     // each type, were removed.
677     for (uint32_t i = 1; i < 5; i++)
678     {
679         Repository::LogID id{Repository::LogID::Pel{i}};
680         EXPECT_FALSE(repo.getPELAttributes(id));
681 
682         // Make sure the corresponding OpenBMC event log ID which is
683         // 500 + the PEL ID is in the list.
684         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
685     }
686 }
687 
688 // Test that if filled completely with 1 type of PEL, that
689 // pruning still works properly
690 TEST_F(RepositoryTest, TestPruneInfoOnly)
691 {
692     Repository repo{repoPath, 4096 * 22, 100};
693 
694     // Fill 4096*23 bytes on disk of BMC info PELs
695     for (uint32_t i = 1; i <= 23; i++)
696     {
697         auto data = pelFactory(i, 'O', 0, 0x8800, 1000);
698         auto pel = std::make_unique<PEL>(data);
699         repo.add(pel);
700     }
701 
702     const auto& sizes = repo.getSizeStats();
703     EXPECT_EQ(sizes.total, 4096 * 23);
704 
705     // Pruning to 15% of 4096 * 22 will leave 3 4096B PELs.
706 
707     // Sanity check the oldest 20 are there so when they
708     // get pruned below we'll know they were removed.
709     for (uint32_t i = 1; i <= 20; i++)
710     {
711         Repository::LogID id{Repository::LogID::Pel{i}};
712         EXPECT_TRUE(repo.getPELAttributes(id));
713     }
714 
715     auto IDs = repo.prune();
716 
717     // Check the final sizes
718     EXPECT_EQ(sizes.total, 4096 * 3);
719     EXPECT_EQ(sizes.bmcInfo, 4096 * 3);
720     EXPECT_EQ(sizes.bmcServiceable, 0);
721     EXPECT_EQ(sizes.nonBMCInfo, 0);
722     EXPECT_EQ(sizes.nonBMCServiceable, 0);
723 
724     EXPECT_EQ(IDs.size(), 20);
725 
726     // Can no longer find the oldest 20 PELs.
727     for (uint32_t i = 1; i <= 20; i++)
728     {
729         Repository::LogID id{Repository::LogID::Pel{i}};
730         EXPECT_FALSE(repo.getPELAttributes(id));
731         EXPECT_TRUE(std::find(IDs.begin(), IDs.end(), 500 + i) != IDs.end());
732     }
733 }
734 
735 // Test that the HMC/OS/PHYP ack values affect the
736 // pruning order.
737 TEST_F(RepositoryTest, TestPruneWithAcks)
738 {
739     Repository repo{repoPath, 4096 * 20, 100};
740 
741     // Fill 30% worth of BMC non-info non-acked PELs
742     for (uint32_t i = 1; i <= 6; i++)
743     {
744         // BMC predictive
745         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
746         auto pel = std::make_unique<PEL>(data);
747         repo.add(pel);
748     }
749 
750     // Add another PEL to push it over the 30%, each time adding
751     // a different type that should be pruned before the above ones
752     // even though those are older.
753     for (uint32_t i = 1; i <= 3; i++)
754     {
755         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
756         auto pel = std::make_unique<PEL>(data);
757         auto idToDelete = pel->obmcLogID();
758         repo.add(pel);
759 
760         if (0 == i)
761         {
762             repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
763         }
764         else if (1 == i)
765         {
766             repo.setPELHostTransState(pel->id(), TransmissionState::acked);
767         }
768         else
769         {
770             repo.setPELHostTransState(pel->id(), TransmissionState::sent);
771         }
772 
773         auto IDs = repo.prune();
774         EXPECT_EQ(repo.getSizeStats().total, 4096 * 6);
775 
776         // The newest PEL should be the one deleted
777         ASSERT_EQ(IDs.size(), 1);
778         EXPECT_EQ(IDs[0], idToDelete);
779     }
780 }
781 
782 // Test that the total number of PELs limit is enforced.
783 TEST_F(RepositoryTest, TestPruneTooManyPELs)
784 {
785     Repository repo{repoPath, 4096 * 100, 10};
786 
787     // Add 10, which is the limit and is still OK
788     for (uint32_t i = 1; i <= 10; i++)
789     {
790         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
791         auto pel = std::make_unique<PEL>(data);
792         repo.add(pel);
793     }
794 
795     auto IDs = repo.prune();
796 
797     // Nothing pruned yet
798     EXPECT_TRUE(IDs.empty());
799 
800     // Add 1 more PEL which will be too many.
801     {
802         auto data = pelFactory(11, 'O', 0x20, 0x8800, 500);
803         auto pel = std::make_unique<PEL>(data);
804         repo.add(pel);
805     }
806 
807     // Now that's it's over the limit of 10, it will bring it down
808     // to 80%, which is 8 after it removes 3.
809     IDs = repo.prune();
810     EXPECT_EQ(repo.getSizeStats().total, 4096 * 8);
811     ASSERT_EQ(IDs.size(), 3);
812 
813     // Check that it deleted the oldest ones.
814     // The OpenBMC log ID is the PEL ID + 500.
815     EXPECT_EQ(IDs[0], 500 + 1);
816     EXPECT_EQ(IDs[1], 500 + 2);
817     EXPECT_EQ(IDs[2], 500 + 3);
818 }
819 
820 // Test the sizeWarning function
821 TEST_F(RepositoryTest, TestSizeWarning)
822 {
823     uint32_t id = 1;
824     Repository repo{repoPath, 100 * 4096, 500};
825 
826     EXPECT_FALSE(repo.sizeWarning());
827 
828     // 95% is still OK (disk size for these is 4096)
829     for (uint32_t i = 1; i <= 95; i++)
830     {
831         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
832         auto pel = std::make_unique<PEL>(data);
833         repo.add(pel);
834     }
835 
836     EXPECT_FALSE(repo.sizeWarning());
837 
838     // Now at 96%
839     auto data = pelFactory(id++, 'B', 0x20, 0x8800, 500);
840     auto pel = std::make_unique<PEL>(data);
841     repo.add(pel);
842 
843     EXPECT_TRUE(repo.sizeWarning());
844 }
845 
846 // Test sizeWarning when there are too many PEls
847 TEST_F(RepositoryTest, TestSizeWarningNumPELs)
848 {
849     Repository repo{repoPath, 4096 * 100, 5};
850 
851     EXPECT_FALSE(repo.sizeWarning());
852 
853     for (uint32_t i = 1; i <= 5; i++)
854     {
855         auto data = pelFactory(i, 'O', 0x20, 0x8800, 500);
856         auto pel = std::make_unique<PEL>(data);
857         repo.add(pel);
858     }
859 
860     EXPECT_FALSE(repo.sizeWarning());
861 
862     // Add 1 more for a total of 6, now over the limit
863     {
864         auto data = pelFactory(6, 'O', 0x20, 0x8800, 500);
865         auto pel = std::make_unique<PEL>(data);
866         repo.add(pel);
867     }
868 
869     EXPECT_TRUE(repo.sizeWarning());
870 }
871