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